Skip to content

Java Chapter 13: Graphs #68

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Feb 6, 2025
29 changes: 29 additions & 0 deletions java/Graphs/BipartiteGraphValidation.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
public class BipartiteGraphValidation {
public boolean bipartiteGraphValidation(int[][] graph) {
int[] colors = new int[graph.length];
// Determine if each graph component is bipartite.
for (int i = 0; i < graph.length; i++) {
if (colors[i] == 0 && !dfs(i, 1, graph, colors)) {
return false;
}
}
return true;
}

private boolean dfs(int node, int color, int[][] graph, int[] colors) {
colors[node] = color;
for (int neighbor : graph[node]) {
// If the current neighbor has the same color as the current
// node, the graph is not bipartite.
if (colors[neighbor] == color) {
return false;
}
// If the current neighbor is not colored, color it with the
// other color and continue the DFS.
if (colors[neighbor] == 0 && !dfs(neighbor, -color, graph, colors)) {
return false;
}
}
return true;
}
}
82 changes: 82 additions & 0 deletions java/Graphs/ConnectTheDots.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

class UnionFind {
int[] parent;
int[] size;

public UnionFind(int size) {
this.parent = new int[size];
this.size = new int[size];
for (int i = 0; i < size; i++) {
this.parent[i] = i;
}
Arrays.fill(this.size, 1);
}

public boolean union(int x, int y) {
int repX = find(x);
int repY = find(y);
if (repX != repY) {
if (this.size[repX] > this.size[repY]) {
this.parent[repY] = repX;
this.size[repX] += this.size[repY];
}
else {
this.parent[repX] = repY;
this.size[repY] += this.size[repX];
}
// Return True if both groups were merged.
return true;
}
// Return False if the points belong to the same group.
return false;
}

public int find(int x) {
if (x == this.parent[x]) {
return x;
}
this.parent[x] = find(this.parent[x]);
return this.parent[x];
}
}

public class ConnectTheDots {
public int connectTheDots(int[][] points) {
int n = points.length;
// Create and populate a list of all possible edges.
List<int[]> edges = new ArrayList<>();
for (int i = 0; i < n; i++) {
for (int j = i + 1; j < n; j++) {
// Manhattan distance.
int cost = Math.abs(points[i][0] - points[j][0]) + Math.abs(points[i][1] - points[j][1]);
edges.add(new int[]{cost, i, j});
}
}
// Sort the edges by their cost in ascending order.
Collections.sort(edges, (a, b) -> Integer.compare(a[0], b[0]));
UnionFind uf = new UnionFind(n);
int totalCost, edgesAdded;
totalCost = edgesAdded = 0;
// Use Kruskal's algorithm to create the MST and identify its minimum cost.
for (int[] edge : edges) {
int cost = edge[0];
int p1 = edge[1];
int p2 = edge[2];
// If the points are not already connected (i.e. their representatives are
// not the same), connect them, and add the cost to the total cost.
if (uf.union(p1, p2)) {
totalCost += cost;
edgesAdded++;
// If n - 1 edges have been added to the MST, the MST is complete.
if (edgesAdded == n - 1) {
return totalCost;
}
}
}
return 0;
}
}
39 changes: 39 additions & 0 deletions java/Graphs/CountIslands.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
public class CountIslands {
public int countIslands(int[][] matrix) {
if (matrix == null || matrix.length == 0 || matrix[0] == null || matrix[0].length == 0) {
return 0;
}
int count = 0;
for (int r = 0; r < matrix.length; r++) {
for (int c = 0; c < matrix[0].length; c++) {
// If a land cell is found, perform DFS to explore the full
// island, and include this island in our count.
if (matrix[r][c] == 1) {
dfs(r, c, matrix);
count++;
}
}
}
return count;
}

private void dfs(int r, int c, int[][] matrix) {
// Mark the current land cell as visited.
matrix[r][c] = -1;
// Define direction vectors for up, down, left, and right.
int[][] dirs = new int[][]{{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
// Recursively call DFS on each neighboring land cell to continue
// exploring this island.
for (int[] d : dirs) {
int nextR = r + d[0];
int nextC = c + d[1];
if (isWithinBounds(nextR, nextC, matrix) && matrix[nextR][nextC] == 1) {
dfs(nextR, nextC, matrix);
}
}
}

private boolean isWithinBounds(int r, int c, int[][] matrix) {
return 0 <= r && r < matrix.length && 0 <= c && c < matrix[0].length;
}
}
46 changes: 46 additions & 0 deletions java/Graphs/GraphDeepCopy.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import java.util.HashMap;
import java.util.Map;

import DS.GraphNode;

/*
// Definition of GraphNode:
class GraphNode {
public int val;
public List<GraphNode> neighbors;
public MultiLevelListNode(int val) {
this.val = val;
this.neighbors = new ArrayList<>();
}
}
*/

public class GraphDeepCopy {
public GraphNode graphDeepCopy(GraphNode node) {
if (node == null) {
return null;
}
Map<GraphNode, GraphNode> cloneMap = new HashMap<>();
return dfs(node, cloneMap);
}

private GraphNode dfs(GraphNode node, Map<GraphNode, GraphNode> cloneMap) {
// If this node was already cloned, then return this previously
// cloned node.
if (cloneMap.containsKey(node)) {
return cloneMap.get(node);
}
// Clone the current node.
GraphNode clonedNode = new GraphNode(node.val);
// Store the current clone to ensure it doesn't need to be created
// again in future DFS calls.
cloneMap.put(node, clonedNode);
// Iterate through the neighbors of the current node to connect
// their clones to the current cloned node.
for (GraphNode neighbor : node.neighbors) {
GraphNode clonedNeighbor = dfs(neighbor, cloneMap);
clonedNode.neighbors.add(clonedNeighbor);
}
return clonedNode;
}
}
43 changes: 43 additions & 0 deletions java/Graphs/LongestIncreasingPath.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
public class LongestIncreasingPath {
public int longestIncreasingPath(int[][] matrix) {
if (matrix == null || matrix.length == 0 || matrix[0] == null || matrix[0].length == 0) {
return 0;
}
int res = 0;
int m = matrix.length;
int n = matrix[0].length;
int[][] memo = new int[m][n];
// Find the longest increasing path starting at each cell. The
// maximum of these is equal to the overall longest increasing
// path.
for (int r = 0; r < m; r++) {
for (int c = 0; c < n; c++) {
res = Math.max(res, dfs(r, c, matrix, memo));
}
}
return res;
}

private int dfs(int r, int c, int[][] matrix, int[][] memo) {
if (memo[r][c] != 0) {
return memo[r][c];
}
int maxPath = 1;
int[][] dirs = new int[][]{{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
// The longest path starting at the current cell is equal to the
// longest path of its larger neighboring cells, plus 1.
for (int[] d : dirs) {
int nextR = r + d[0];
int nextC = c + d[1];
if (isWithinBounds(nextR, nextC, matrix) && matrix[nextR][nextC] > matrix[r][c]) {
maxPath = Math.max(maxPath, 1 + dfs(nextR, nextC, matrix, memo));
}
}
memo[r][c] = maxPath;
return maxPath;
}

private boolean isWithinBounds(int r, int c, int[][] matrix) {
return 0 <= r && r < matrix.length && 0 <= c && c < matrix[0].length;
}
}
53 changes: 53 additions & 0 deletions java/Graphs/MatrixInfection.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import java.util.LinkedList;
import java.util.Queue;

public class MatrixInfection {
public int matrixInfection(int[][] matrix) {
int[][] dirs = new int[][]{{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
Queue<int[]> queue = new LinkedList<>();
int ones, seconds;
ones = seconds = 0;
// Count the total number of uninfected cells and add each infected
// cell to the queue to represent level 0 of the level-order
// traversal.
for (int r = 0; r < matrix.length; r++) {
for (int c = 0; c < matrix[0].length; c++) {
if (matrix[r][c] == 1) {
ones += 1;
} else if (matrix[r][c] == 2) {
queue.offer(new int[]{r, c});
}
}
}
// Use level-order traversal to determine how long it takes to
// infect the uninfected cells.
while (!queue.isEmpty() && ones > 0) {
// 1 second passes with each level of the matrix that's explored.
seconds++;
int size = queue.size();
for (int i = 0; i < size; i++) {
int[] pos = queue.poll();
int r = pos[0];
int c = pos[1];
// Infect any neighboring 1s and add them to the queue to be
// processed in the next level.
for (int[] d : dirs) {
int nextR = r + d[0];
int nextC = c + d[1];
if (isWithinBounds(nextR, nextC, matrix) && matrix[nextR][nextC] == 1) {
matrix[nextR][nextC] = 2;
ones--;
queue.offer(new int[]{nextR, nextC});
}
}
}
}
// If there are still uninfected cells left, return -1. Otherwise,
// return the time passed.
return ones == 0 ? seconds : -1;
}

private boolean isWithinBounds(int r, int c, int[][] matrix) {
return 0 <= r && r < matrix.length && 0 <= c && c < matrix[0].length;
}
}
62 changes: 62 additions & 0 deletions java/Graphs/MergingCommunities.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import java.util.Arrays;

class UnionFind {
int[] parent;
int[] size;

public UnionFind(int size) {
this.parent = new int[size];
this.size = new int[size];
for (int i = 0; i < size; i++) {
this.parent[i] = i;
}
Arrays.fill(this.size, 1);
}

public void union(int x, int y) {
int repX = find(x);
int repY = find(y);
if (repX != repY) {
// If 'repX' represents a larger community, connect
// 'repY 's community to it.
if (this.size[repX] > this.size[repY]) {
this.parent[repY] = repX;
this.size[repX] += this.size[repY];
}
// Otherwise, connect 'rep_x's community to 'rep_y'.
else {
this.parent[repX] = repY;
this.size[repY] += this.size[repX];
}
}
}

public int find(int x) {
if (x == this.parent[x]) {
return x;
}
// Path compression.
this.parent[x] = find(this.parent[x]);
return this.parent[x];
}

public int getSize(int x) {
return this.size[find(x)];
}
}

public class MergingCommunities {
UnionFind uf;

public MergingCommunities(int n) {
this.uf = new UnionFind(n);
}

public void connect(int x, int y) {
this.uf.union(x, y);
}

public int getCommunitySize(int x) {
return this.uf.getSize(x);
}
}
Loading