diff --git a/FloodFill.java b/FloodFill.java new file mode 100644 index 00000000..7eee22b7 --- /dev/null +++ b/FloodFill.java @@ -0,0 +1,132 @@ +import java.util.LinkedList; +import java.util.Queue; +import java.util.Arrays; + +/* +LeetCode 733. Flood Fill +*/ +public class FloodFill { + + private int[][] dirs; + private int m, n; + + /* + APPROACH 1: BFS (Queue) + Idea: + - Start from (sr, sc), recolor it, push it into a queue. + - Pop pixels one-by-one, expand to 4 neighbors. + - If a neighbor has oldColor, recolor it and push it. + + Time Complexity: O(m * n) + - each cell is processed at most once + + Space Complexity: O(m * n) worst-case + - queue may hold many cells (one large region) + */ + public int[][] floodFill(int[][] image, int sr, int sc, int color) { + dirs = new int[][]{{-1, 0}, {1, 0}, {0, 1}, {0, -1}}; + m = image.length; + n = image[0].length; + + int oldColor = image[sr][sc]; + if (oldColor == color) return image; // no change needed + + Queue q = new LinkedList<>(); + q.add(new int[]{sr, sc}); + image[sr][sc] = color; // mark visited by recoloring + + while (!q.isEmpty()) { + int[] curr = q.poll(); + + for (int[] d : dirs) { + int newR = curr[0] + d[0]; + int newC = curr[1] + d[1]; + + // boundary + only recolor cells that match oldColor + if (newR >= 0 && newR < m && newC >= 0 && newC < n && image[newR][newC] == oldColor) { + image[newR][newC] = color; + q.add(new int[]{newR, newC}); + } + } + } + + return image; + } + + /* + APPROACH 2: DFS (Recursion) + Idea: + - Recolor current cell. + - Recurse on 4 neighbors if they are in bounds and match oldColor. + + Time Complexity: O(m * n) + Space Complexity: O(m * n) worst-case due to recursion stack (large region / deep traversal) + - balanced regions typically smaller, but worst-case can be big + */ + public int[][] floodFillUsingDFS(int[][] image, int sr, int sc, int color) { + dirs = new int[][]{{-1, 0}, {1, 0}, {0, 1}, {0, -1}}; + m = image.length; + n = image[0].length; + + int oldColor = image[sr][sc]; + if (oldColor == color) return image; + + dfs(image, sr, sc, color, oldColor); + return image; + } + + private void dfs(int[][] image, int r, int c, int newColor, int oldColor) { + // out of bounds OR not part of the region + if (r < 0 || c < 0 || r >= m || c >= n || image[r][c] != oldColor) return; + + // recolor current pixel + image[r][c] = newColor; + + // visit neighbors + for (int[] d : dirs) { + dfs(image, r + d[0], c + d[1], newColor, oldColor); + } + } + + // Simple main method to test (no assertions) + public static void main(String[] args) { + FloodFill solver = new FloodFill(); + + int[][] image = { + {1, 1, 1}, + {1, 1, 0}, + {1, 0, 1} + }; + + int sr = 1, sc = 1, color = 2; + + // Make copies because methods mutate the image + int[][] imageForBFS = copy(image); + int[][] imageForDFS = copy(image); + + System.out.println("Original image:"); + print(image); + + System.out.println("\nBFS Flood Fill result:"); + solver.floodFill(imageForBFS, sr, sc, color); + print(imageForBFS); + + System.out.println("\nDFS Flood Fill result:"); + solver.floodFillUsingDFS(imageForDFS, sr, sc, color); + print(imageForDFS); + } + + private static int[][] copy(int[][] img) { + int[][] res = new int[img.length][img[0].length]; + for (int i = 0; i < img.length; i++) { + System.arraycopy(img[i], 0, res[i], 0, img[0].length); + } + return res; + } + + private static void print(int[][] img) { + for (int[] row : img) { + System.out.println(Arrays.toString(row)); + } + } +} diff --git a/NearestZero.java b/NearestZero.java new file mode 100644 index 00000000..7ac0a1b4 --- /dev/null +++ b/NearestZero.java @@ -0,0 +1,163 @@ +import java.util.LinkedList; +import java.util.Queue; +import java.util.Arrays; + +/* +LeetCode 542. 01 Matrix (Nearest Zero) +Given an m x n binary matrix mat, return a matrix where each cell contains the distance +to the nearest 0 +Two BFS-based approaches: + +APPROACH 1 (Brute Force BFS from each 1) +- For every cell that is 1, run BFS until you find a 0. +- Correct but slow because BFS repeats many times. + +Time Complexity: O((m*n) * (m*n)) = O((m*n)^2) worst-case +Space Complexity: O(m*n) for visited + queue per BFS + +APPROACH 2 (Optimal Multi-source BFS) +- Put all zeros into a queue initially (all as BFS sources). +- Distances for zeros are 0. +- BFS expands outward; first time we reach a cell is its shortest distance to a zero. + +Time Complexity: O(m*n) +Space Complexity: O(m*n) +*/ +public class NearestZero { + + private int[][] dirs; + private int m, n; + + // -------------------- Approach 1: BFS from each '1' cell (Brute Force) -------------------- + public int[][] updateMatrix(int[][] mat) { + if (mat == null || mat.length == 0) return mat; + + this.m = mat.length; + this.n = mat[0].length; + dirs = new int[][]{{-1, 0}, {1, 0}, {0, -1}, {0, 1}}; + + // For each 1, run BFS to find nearest 0 and overwrite the cell with the distance + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + if (mat[i][j] == 1) { + mat[i][j] = bfs(mat, i, j); + } + } + } + return mat; + } + + // BFS that starts from one cell (i, j) that is 1 and returns distance to nearest 0 + private int bfs(int[][] mat, int i, int j) { + Queue q = new LinkedList<>(); + q.add(new int[]{i, j}); + + boolean[][] visited = new boolean[m][n]; + visited[i][j] = true; + + int dist = 1; + + // Standard level-order BFS: each layer increases distance by 1 + while (!q.isEmpty()) { + int size = q.size(); + + for (int k = 0; k < size; k++) { + int[] curr = q.poll(); + + for (int[] d : dirs) { + int r = curr[0] + d[0]; + int c = curr[1] + d[1]; + + if (r >= 0 && r < m && c >= 0 && c < n) { + // If we reach a 0, dist is the shortest distance because BFS expands by layers + if (mat[r][c] == 0) return dist; + + // Otherwise keep expanding through unvisited cells + if (!visited[r][c]) { + visited[r][c] = true; + q.add(new int[]{r, c}); + } + } + } + } + dist++; + } + + return -1; // should not happen if at least one zero exists in matrix + } + + public int[][] updateMatrixMultiSourceBFS(int[][] mat) { + if (mat == null || mat.length == 0) return mat; + + int m = mat.length, n = mat[0].length; + int[][] dirs = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}}; + + Queue q = new LinkedList<>(); + + // Initialize: + // - enqueue all zeros + // - mark ones as -1 (unvisited) + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + if (mat[i][j] == 0) { + q.add(new int[]{i, j}); + } else { + mat[i][j] = -1; + } + } + } + + // BFS expanding from all zeros + while (!q.isEmpty()) { + int[] cur = q.poll(); + int r = cur[0], c = cur[1]; + + for (int[] d : dirs) { + int nr = r + d[0]; + int nc = c + d[1]; + + if (nr >= 0 && nr < m && nc >= 0 && nc < n && mat[nr][nc] == -1) { + // First time reaching this cell => shortest distance + mat[nr][nc] = mat[r][c] + 1; + q.add(new int[]{nr, nc}); + } + } + } + + return mat; + } + + // Simple main method to test (no assertions) + public static void main(String[] args) { + NearestZero solver = new NearestZero(); + + int[][] mat = { + {0, 0, 0}, + {0, 1, 0}, + {1, 1, 1} + }; + + System.out.println("Original:"); + print(mat); + int[][] mat1 = copy(mat); + System.out.println("\nApproach 1 (BFS from each 1):"); + print(solver.updateMatrix(mat1)); + int[][] mat2 = copy(mat); + System.out.println("\nApproach 2 (Multi-source BFS):"); + print(solver.updateMatrixMultiSourceBFS(mat2)); + } + + private static int[][] copy(int[][] a) { + int[][] res = new int[a.length][a[0].length]; + for (int i = 0; i < a.length; i++) { + System.arraycopy(a[i], 0, res[i], 0, a[0].length); + } + return res; + } + + private static void print(int[][] a) { + for (int[] row : a) { + System.out.println(Arrays.toString(row)); + } + } +}