Matrix

10. Walls and Gates

  1. Problem Description: The problem "Walls and Gates" on LeetCode can be found at the following link: https://leetcode.com/problems/walls-and-gates/

In this problem, you are given a 2D grid containing walls, gates, and empty rooms. You need to fill each empty room with the distance to the nearest gate. If it is impossible to reach a gate, leave the room as INF (infinity).

  1. Optimized Java Code:

Here's the optimized Java code for the "Walls and Gates" problem:

import java.util.LinkedList;
import java.util.Queue;

class Solution {
    public void wallsAndGates(int[][] rooms) {
        if (rooms == null || rooms.length == 0 || rooms[0].length == 0) {
            return;
        }
        
        int numRows = rooms.length;
        int numCols = rooms[0].length;
        
        // Create a queue to store empty room coordinates
        Queue<int[]> queue = new LinkedList<>();
        
        // Add all gate coordinates to the queue
        for (int i = 0; i < numRows; i++) {
            for (int j = 0; j < numCols; j++) {
                if (rooms[i][j] == 0) {
                    queue.offer(new int[] {i, j});
                }
            }
        }
        
        // Possible directions to move in the grid
        int[][] directions = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
        
        // Perform a BFS traversal starting from each gate
        while (!queue.isEmpty()) {
            int[] curr = queue.poll();
            int row = curr[0];
            int col = curr[1];
            
            for (int[] dir : directions) {
                int newRow = row + dir[0];
                int newCol = col + dir[1];
                
                // Check if the new coordinates are valid
                if (newRow >= 0 && newRow < numRows && newCol >= 0 && newCol < numCols && rooms[newRow][newCol] == Integer.MAX_VALUE) {
                    // Update the distance to the nearest gate
                    rooms[newRow][newCol] = rooms[row][col] + 1;
                    queue.offer(new int[] {newRow, newCol});
                }
            }
        }
    }
}
  1. Detailed Description: The problem can be solved using a breadth-first search (BFS) algorithm. We start by adding all gate coordinates to a queue, and then perform a BFS traversal starting from each gate. During the traversal, we update the distances of empty rooms to the nearest gate.

Here's a step-by-step breakdown of the algorithm:

  • Check if the input grid rooms is empty or null. If it is, return immediately.

  • Get the number of rows and columns in the grid.

  • Create a queue to store empty room coordinates.

  • Iterate through the grid to find all gate coordinates (marked with a value of 0), and add them to the queue.

  • Define the possible directions to move in the grid: up, down, left, and right.

  • Perform a BFS traversal starting from each gate in the queue. In each iteration of the traversal, we remove a coordinate from the queue and explore its adjacent rooms.

  • For each adjacent room that is an empty room (marked with a value of infinity), update its distance to the nearest gate by incrementing the distance of the current room by 1.

  • Add the updated empty room coordinates to the queue.

  • Repeat the above steps until the queue is empty, and all empty rooms have been visited.

The time complexity of this algorithm is O(n), where n represents the number of cells in the grid.

11. Rotting Oranges

  1. The problem "Rotting Oranges" on LeetCode can be found here: https://leetcode.com/problems/rotting-oranges/

  2. Here's the optimized Java code for the problem "Rotting Oranges":

import java.util.LinkedList;
import java.util.Queue;

class Solution {
    public int orangesRotting(int[][] grid) {
        // Number of rows
        int rows = grid.length;
        // Number of columns
        int cols = grid[0].length;
        // Variable to store the number of fresh oranges
        int freshCount = 0;
        
        // Queue to keep track of the rotten oranges
        Queue<int[]> queue = new LinkedList<>();
        
        // Count number of fresh oranges and add rotten oranges to the queue
        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < cols; j++) {
                if (grid[i][j] == 1)
                    freshCount++;
                else if (grid[i][j] == 2)
                    queue.offer(new int[] {i, j});
            }
        }
        
        // If there are no fresh oranges, return 0
        if (freshCount == 0)
            return 0;
        
        // Array to find the 4 adjacent positions of a cell
        int[][] directions = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
        
        int minutes = 0;
        
        while (!queue.isEmpty()) {
            // Number of rotten oranges at the current minute
            int rottenCount = queue.size();
            
            for (int i = 0; i < rottenCount; i++) {
                // Get the coordinates of a rotten orange
                int[] curr = queue.poll();
                
                // Check the 4 adjacent positions of the rotten orange
                for (int[] dir : directions) {
                    int nextRow = curr[0] + dir[0];
                    int nextCol = curr[1] + dir[1];
                    
                    // If the next position is within grid boundaries
                    // and it has a fresh orange, convert it to rotten
                    if (nextRow >= 0 && nextRow < rows && nextCol >= 0 && nextCol < cols &&
                        grid[nextRow][nextCol] == 1) {
                        
                        // Convert fresh orange to rotten orange
                        grid[nextRow][nextCol] = 2;
                        // Decrement the count of fresh oranges
                        freshCount--;
                        // Add the coordinate of the newly rotten orange to the queue
                        queue.offer(new int[] {nextRow, nextCol});
                    }
                }
            }
            
            // Increment the time at the end of each minute
            if (!queue.isEmpty())
                minutes++;
        }
        
        // If there are still fresh oranges left, it's impossible to rot all oranges
        if (freshCount > 0)
            return -1;
        
        return minutes;
    }
}
  1. Algorithm and Interview Approach:

    • The problem can be solved using Breadth First Search (BFS) approach.

    • The main idea is to simulate the process of rotting the oranges using a queue.

    • First, count the number of fresh oranges and add the coordinates of the initially rotten oranges to the queue.

    • Then, process the queue until it becomes empty, while keeping track of the number of rotten oranges at each minute.

    • For each rotten orange, check its adjacent positions (up, down, left, right) using the directions array.

    • If the adjacent position contains a fresh orange, convert it to a rotten orange, decrement the count of fresh oranges, and add it to the queue.

    • Continue this process until the queue becomes empty.

    • At the end, if there are still fresh oranges left, it means it's not possible to rot all the oranges, so return -1.

    • Otherwise, return the number of minutes it took to rot all the oranges.

    • During the interview, it is important to discuss the edge cases and validate the input constraints with the interviewer.

    • Also, discussing the time complexity, space complexity, and possible optimizations can be beneficial.

12. Number of Islands II

  1. Problem Description: The problem "Number of Islands II" on LeetCode asks us to find the number of islands formed as we progressively add land cells to a grid.

Problem Link: https://leetcode.com/problems/number-of-islands-ii/

  1. Optimized Java Solution:

class Solution {
    public List<Integer> numIslands2(int m, int n, int[][] positions) {
        List<Integer> result = new ArrayList<>();
        if (m <= 0 || n <= 0) {
            return result;
        }
        
        int count = 0;
        int[] roots = new int[m * n];
        Arrays.fill(roots, -1);
        int[][] directions = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
        
        for (int[] position : positions) {
            int row = position[0];
            int col = position[1];
            int root = n * row + col;
            
            if (roots[root] == -1) {
                roots[root] = root;
                count++;
                
                for (int[] direction : directions) {
                    int newRow = row + direction[0];
                    int newCol = col + direction[1];
                    int newRoot = n * newRow + newCol;
                    
                    if (newRow < 0 || newRow >= m || newCol < 0 || newCol >= n || roots[newRoot] == -1) {
                        continue;
                    }
                    int root1 = findIslandRoot(roots, root);
                    int root2 = findIslandRoot(roots, newRoot);
                    
                    if (root1 != root2) {
                        roots[root1] = root2;
                        count--;
                    }
                }
            }
            result.add(count);
        }
        return result;
    }
    
    private int findIslandRoot(int[] roots, int id) {
        while (id != roots[id]) {
            roots[id] = roots[roots[id]];
            id = roots[id];
        }
        return id;
    }
}
  1. Detailed Description and Interview Process:

The algorithm used in this problem is "Union-Find" also known as "Disjoint Set Union". The idea is to keep track of groups of connected cells (islands) by maintaining a list of roots for each cell. Initially, all the roots are set to -1, indicating that each cell is a separate island.

For each added cell (position), we first check if it is already on the grid and if it is a new cell. If it is a new cell, we set its root as itself and increment the count of islands. Then, we check the four neighboring cells and union the two roots if they are not already in the same island. We update the count if a union is performed.

The findIslandRoot function is used to find the root of any given cell. It performs path compression, which helps optimize future root finding queries by reducing the height of the tree structure.

During the interview process, it is important to understand the problem requirements, edge cases, and constraints. This problem tests our understanding of the union-find concept and its application in finding the number of islands in a grid. It also tests our ability to implement the solution efficiently.

Here are some key points to keep in mind while explaining this problem during an interview:

  • Clearly describe the problem requirements and constraints, and provide examples to illustrate the problem.

  • Explain the algorithm used and its time complexity. In this case, "Union-Find" is used, which has a time complexity of O(k * α(n)), where k is the total number of cells and α(n) is the inverse Ackerman function (almost constant).

  • Describe the implementation details, including the data structures used (arrays in this case).

  • Discuss the importance of path compression in the findIslandRoot function and its impact on the time complexity.

  • Explain the logic behind the union operation and how it helps in counting the number of islands.

By explaining the problem and the optimized solution with detailed comments and descriptions, you can demonstrate your understanding of the problem-solving process and your ability to communicate your thoughts effectively during an interview.

13. Surrounded Regions

  1. Problem Description:

The problem "Surrounded Regions" on LeetCode can be found at the following link:

https://leetcode.com/problems/surrounded-regions/

  1. Optimized Java code for the "Surrounded Regions" problem with detailed comments:

class Solution {
    public void solve(char[][] board) {
        if (board == null || board.length == 0) return;
        
        int rows = board.length;
        int cols = board[0].length;
        
        // Mark 'O' connected to boundaries as '1'
        for (int i = 0; i < rows; i++) {
            if (board[i][0] == 'O') {
                dfs(board, i, 0);
            }
            if (board[i][cols - 1] == 'O') {
                dfs(board, i, cols - 1);
            }
        }
        
        for (int j = 0; j < cols; j++) {
            if (board[0][j] == 'O') {
                dfs(board, 0, j);
            }
            if (board[rows - 1][j] == 'O') {
                dfs(board, rows - 1, j);
            }
        }
        
        // Convert 'O' to 'X' and '1' to 'O'
        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < cols; j++) {
                if (board[i][j] == 'O') {
                    board[i][j] = 'X';
                }
                if (board[i][j] == '1') {
                    board[i][j] = 'O';
                }
            }
        }
    }
    
    private void dfs(char[][] board, int row, int col) {
        if (row < 0 || col < 0 || row >= board.length || col >= board[0].length || board[row][col] != 'O') {
            return;
        }
        
        board[row][col] = '1'; // Mark 'O' as '1' to avoid double counting
        
        // Recursively check adjacent cells
        dfs(board, row + 1, col);
        dfs(board, row - 1, col);
        dfs(board, row, col + 1);
        dfs(board, row, col - 1);
    }
}
  1. Description of the algorithm and approach:

  • We are given a 2D board where 'X' represents walls and 'O' represents empty spaces. Our goal is to capture all regions of empty spaces surrounded by walls and mark them as 'X'.

  • To solve this problem, we can use a Depth First Search (DFS) algorithm. We need to identify the regions of 'O' that are connected to the boundary of the board, as these regions cannot be surrounded by walls and should be marked as 'O'.

  • We start by iterating through the boundary of the board. If we find an 'O', we perform DFS from that cell and mark all connected 'O' cells as '1'.

  • After marking all 'O' cells connected to the boundary, we iterate through the entire board again. We convert all remaining 'O' cells to 'X' and restore the marked '1' cells back to 'O'.

  • The time complexity of this solution is O(rows * cols) as we need to visit each cell of the board once. The space complexity is O(1) as we are modifying the input board in-place without using any additional data structures.

  • During an interview, it is important to mention the steps of the algorithm and explain the reasoning behind them. It is also recommended to discuss the time and space complexity. Additionally, letting the interviewer know that the solution is optimized by modifying the input in-place and not using any extra space can showcase your problem-solving skills.

Last updated