Open In App

POTD Solutions | 16 Oct’ 23 | Making A Large Island

Last Updated : 19 Oct, 2023
Improve
Improve
Like Article
Like
Save
Share
Report

Welcome to the daily solutions of our PROBLEM OF THE DAY (POTD). We will discuss the entire problem step-by-step and work towards developing an optimized solution. This will not only help you brush up on your concepts of DFS and Disjoint Set Union but will also help you build up problem-solving skills.

POTD 16 October: Making A Large Island:

Given a binary matrix of size n x n, the task is to find the largest island after changing at most one cell from 0 to 1.

Note: An island is a 4-directionally (i.e, up, down, right, left) connected group of 1s.

Example 1:

Example 2:

We recommend you to try this problem on our GeeksforGeeks Practice portal first, and maintain your streak to earn Geeksbits and other exciting prizes, before moving towards the solution.

Maximum Connected group using DFS

The idea is to keep track of each island by some unique identity say key/name with its size. After this, go through each cell which is water (i.e, matrix[i][j] == 0) and add the sizes of unique neighbour’s island into current size, finally maximize the current size in the result.

Follow the steps below to implement the above idea:

  • Initialize a result variable for storing the largest island.
  • Create an unordered map to store identity of each island with its sizes.
  • Explore each island and update the cell’s value with a unique key and finally store key and size of island in map.
  • Iterate through each cell of the grid. which represents water or 0’s:
    • Get sizes of neighboring islands from the map.
    • Calculate combined size of neighboring islands and the current cell.
    • Update the result with the maximum calculated size.

Below is the implementation of the above approach:

C++




class Solution {
public:
    // Initialize an unordered map to store island
    // sizes with corresponding keys.
    unordered_map<int, int> unmap;
  
    // Depth-first search (DFS) function to calculate
    // the size of an island.
    int dfs(int i, int j, vector<vector<int> >& grid,
            vector<vector<bool> >& visited, int key)
    {
        int n = grid.size();
  
        // Base cases: Check for boundaries,
        // visited status, and water (grid[i][j] == 0).
        if (i < 0 || j < 0 || i >= n || j >= n
            || visited[i][j] || grid[i][j] == 0) {
            return 0;
        }
  
        // Mark the current cell as visited.
        visited[i][j] = true;
  
        // Recursively explore adjacent cells and
        // accumulate the island size.
        int count = 1 + dfs(i + 1, j, grid, visited, key)
                    + dfs(i - 1, j, grid, visited, key)
                    + dfs(i, j + 1, grid, visited, key)
                    + dfs(i, j - 1, grid, visited, key);
  
        // Update the cell's value to the key
        // representing the island.
        grid[i][j] = key;
        return count;
    }
  
    // Function to find the size of the largest
    // island in the grid.
    int largestIsland(vector<vector<int> >& grid)
    {
        int n = grid.size();
        int key = 2;
        vector<vector<bool> > visited(
            n, vector<bool>(n, false));
  
        // Traverse the grid to identify and mark
        // islands, and store their sizes in the map.
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                if (visited[i][j] == false
                    && grid[i][j] == 1) {
                    int count
                        = dfs(i, j, grid, visited, key);
  
                    // Store island size in the map.
                    unmap[key] = count;
                    key++;
                }
            }
        }
  
        int result = -1;
        vector<vector<bool> > visited2(
            n, vector<bool>(n, false));
  
        // Traverse the grid again to identify water
        // cells and calculate the largest
        // possible island.
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                if (grid[i][j] == 0) {
  
                    // Check adjacent cells and
                    // gather island sizes from the map.
                    int a
                        = (i + 1 < n) ? grid[i + 1][j] : 0;
                    int b
                        = (i - 1 >= 0) ? grid[i - 1][j] : 0;
                    int c
                        = (j + 1 < n) ? grid[i][j + 1] : 0;
                    int d
                        = (j - 1 >= 0) ? grid[i][j - 1] : 0;
  
                    // Store unique island sizes
                    // around the current water cell.
                    set<int> st;
                    st.insert(a);
                    st.insert(b);
                    st.insert(c);
                    st.insert(d);
  
                    int res = 1;
  
                    // Calculate the combined size
                    // of neighboring islands.
                    for (auto it = st.begin();
                         it != st.end(); it++) {
                        res += unmap[*it];
                    }
  
                    // Update the largest island size.
                    result = max(result, res);
                }
            }
        }
  
        // If no land cells were present (only water),
        // return the size of the entire grid.
        if (result == -1) {
            return n * n;
        }
        return result;
    }
};


Java




class Solution {
  
    // Initialize a hashmap to store island sizes with
    // corresponding keys.
    HashMap<Integer, Integer> map = new HashMap<>();
  
    // Depth-first search (DFS) function to calculate the
    // size of an island.
    public int dfs(int i, int j, int[][] grid,
                   boolean[][] visited, int key)
    {
        int n = grid.length;
  
        // Base cases: Check for boundaries, visited status,
        // and water (grid[i][j] == 0).
        if (i < 0 || j < 0 || i >= n || j >= n
            || visited[i][j] || grid[i][j] == 0) {
            return 0;
        }
  
        // Mark the current cell as visited.
        visited[i][j] = true;
  
        // Recursively explore adjacent cells and accumulate
        // the island size.
        int count = 1 + dfs(i + 1, j, grid, visited, key)
                    + dfs(i - 1, j, grid, visited, key)
                    + dfs(i, j + 1, grid, visited, key)
                    + dfs(i, j - 1, grid, visited, key);
  
        // Update the cell's value to the key representing
        // the island.
        grid[i][j] = key;
        return count;
    }
  
    public int largestIsland(int N, int[][] grid)
    {
  
        int key = 2;
        boolean[][] visited = new boolean[N][N];
  
        // Traverse the grid to identify and mark islands,
        // and store their sizes in the map.
        for (int i = 0; i < N; i++) {
            for (int j = 0; j < N; j++) {
                if (!visited[i][j] && grid[i][j] == 1) {
                    int count
                        = dfs(i, j, grid, visited, key);
  
                    // Store island size in the map.
                    map.put(key, count);
                    key++;
                }
            }
        }
  
        int result = -1;
        boolean[][] visited2 = new boolean[N][N];
  
        // Traverse the grid again to identify water cells
        // and calculate the largest possible island.
        for (int i = 0; i < N; i++) {
            for (int j = 0; j < N; j++) {
                if (grid[i][j] == 0) {
  
                    // Check adjacent cells and gather
                    // island sizes from the map.
                    int a
                        = (i + 1 < N) ? grid[i + 1][j] : 0;
                    int b
                        = (i - 1 >= 0) ? grid[i - 1][j] : 0;
                    int c
                        = (j + 1 < N) ? grid[i][j + 1] : 0;
                    int d
                        = (j - 1 >= 0) ? grid[i][j - 1] : 0;
  
                    // Store unique island sizes around the
                    // current water cell.
                    HashSet<Integer> set = new HashSet<>();
                    set.add(a);
                    set.add(b);
                    set.add(c);
                    set.add(d);
  
                    int res = 1;
  
                    // Calculate the combined size of
                    // neighboring islands.
                    for (int val : set) {
                        res += map.getOrDefault(val, 0);
                    }
  
                    // Update the largest island size.
                    result = Math.max(result, res);
                }
            }
        }
  
        // If no land cells were present (only water),
        // return the size of the entire grid.
        if (result == -1) {
            return N * N;
        }
        return result;
    }
}


Time Complexity: O(n2)

  • The two nested loops iterate through all cells in the grid, resulting in O(n2) iterations.
  • Inside the loops, constant time operations are performed for DFS exploration and island size calculations.
  • The overall time complexity is O(n2), where n is the size of the grid.

Auxiliary Space: O(n2)

  • The visited matrix requires O(n2) space.
  • The unordered map stores island sizes, potentially up to O(n2) distinct islands.
  • Other variables and data structures used occupy constant space.
  • The overall space complexity is O(n2).

Maximum Connected group using DSU

The idea is to use disjoint set union to find our answer we can do this by first making add the islands as the union and storing the size of each island and then we can find the sum of the size of adjacent island for all the values in the grid which are 0.

  • Initialize a disjoint class ds and two vector dr and dc which are used to move the four adjacent cells for any cell.
  • Traverse through the grid,
    • If we get the grid cell with value 1, then we move to its four adjacent cells and make the Union by using the unionBySize function of current cell and the adjacent cells.
  • Traverse through the grid,
    • If we get a cell with the value 0 then we see all of its four adjacent neighbours, if any of its neighbours is 1 then for each we insert the the parent of that adjacent node into the set using the findUPar function.
    • Intialize a variable sizeTotal equal to 0 and then we iterate the set and add the size of the parent cells into the sizeTotal.
    • Update the mx with maximum of mx and sizeTotal+1, we add 1 as the current cell is also included.
  • But if the matrix contains only 1 then to consider that we traverse for every node from 0 to n*n and find the size of its parent and then update mx with maximum of mx and size of the parent.
  • Return mx.

C++




class DisjointSet {
public:
    vector<int> rank, parent, size;
  
    // Constructor to initialize the disjoint set data
    // structure
    DisjointSet(int n)
    {
        rank.resize(n + 1, 0);
        parent.resize(n + 1);
        size.resize(n + 1);
  
        for (int i = 0; i <= n; i++) {
            parent[i] = i; // Initialize each element as its
            // own parent
            size[i]
                = 1; // Initialize the size of each set as 1
        }
    }
  
    // Find the parent of a node with path compression
    int findUPar(int node)
    {
        if (node == parent[node])
            return node;
        return parent[node] = findUPar(parent[node]);
    }
  
    // Union two sets by size, with path compression
    void unionBySize(int u, int v)
    {
        u = findUPar(u);
        v = findUPar(v);
        if (u == v)
            return;
  
        if (size[u] < size[v]) {
            parent[u] = v;
            size[v] += size[u];
        }
        else {
            parent[v] = u;
            size[u] += size[v];
        }
    }
};
  
class Solution {
private:
    // Check if a cell is within the grid bounds
    bool isValid(int r, int c, int n)
    {
        return r >= 0 && r < n && c >= 0 && c < n;
    }
  
public:
    // Calculate the size of the largest island in the grid
    int largestIsland(vector<vector<int> >& grid)
    {
        int n = grid.size();
        DisjointSet ds(n * n);
        vector<int> dr = { -1, 0, 1, 0 };
        vector<int> dc = { 0, -1, 0, 1 };
  
        // Union connected land cells using the DisjointSet
        // data structure
        for (int row = 0; row < n; row++) {
            for (int col = 0; col < n; col++) {
                if (grid[row][col] == 0)
                    continue;
  
                for (int ind = 0; ind < 4; ind++) {
                    int r = row + dr[ind];
                    int c = col + dc[ind];
  
                    if (isValid(r, c, n)
                        && grid[r] == 1) {
                        int nodeNo = row * n + col;
                        int adjNodeNo = r * n + c;
                        ds.unionBySize(nodeNo, adjNodeNo);
                    }
                }
            }
        }
  
        int mx = 0;
  
        // Find the size of the largest island by examining
        // each water cell
        for (int row = 0; row < n; row++) {
            for (int col = 0; col < n; col++) {
                if (grid[row][col] == 1)
                    continue;
  
                set<int> components;
  
                // Check neighboring land cells
                for (int ind = 0; ind < 4; ind++) {
                    int r = row + dr[ind];
                    int c = col + dc[ind];
  
                    if (isValid(r, c, n)) {
                        if (grid[r] == 1) {
                            components.insert(
                                ds.findUPar(r * n + c));
                        }
                    }
                }
  
                int sizeTotal = 0;
  
                // Calculate the total size of connected
                // land components
                for (auto it : components) {
                    sizeTotal += ds.size[it];
                }
  
                // Update the maximum size
                mx = max(mx, sizeTotal + 1);
            }
        }
  
        // Find the maximum size among all sets
        for (int cellNo = 0; cellNo < n * n; cellNo++) {
            mx = max(mx, ds.size[ds.findUPar(cellNo)]);
        }
  
        return mx;
    }
};


Java




class DisjointSet {
    private int[] rank;
    private int[] parent;
    private int[] size;
  
    // Constructor to initialize the disjoint set data structure
    public DisjointSet(int n)
    {
        rank = new int[n + 1];
        parent = new int[n + 1];
        size = new int[n + 1];
  
        for (int i = 0; i <= n; i++) {
            parent[i] = i; // Initialize each element as its own parent
            size[i] = 1; // Initialize the size of each set as 1
        }
    }
  
    // Find the parent of a node with path compression
    public int findUPar(int node)
    {
        if (node == parent[node])
            return node;
        return parent[node] = findUPar(parent[node]);
    }
  
    // Union two sets by size, with path compression
    public void unionBySize(int u, int v)
    {
        u = findUPar(u);
        v = findUPar(v);
        if (u == v)
            return;
  
        if (size[u] < size[v]) {
            parent[u] = v;
            size[v] += size[u];
        }
        else {
            parent[v] = u;
            size[u] += size[v];
        }
    }
  
    // Get the size of the set containing the given node
    public int getSize(int node)
    {
        return size[findUPar(node)];
    }
}
  
class Solution {
    // Check if a cell is within the grid bounds
    private boolean isValid(int r, int c, int n)
    {
        return r >= 0 && r < n && c >= 0 && c < n;
    }
  
    public int largestIsland(int n, int[][] grid)
    {
        DisjointSet ds = new DisjointSet(n * n);
        int[] dr = { -1, 0, 1, 0 };
        int[] dc = { 0, -1, 0, 1 };
  
        // Union connected land cells using the DisjointSet data structure
        for (int row = 0; row < n; row++) {
            for (int col = 0; col < n; col++) {
                if (grid[row][col] == 0)
                    continue;
  
                for (int ind = 0; ind < 4; ind++) {
                    int r = row + dr[ind];
                    int c = col + dc[ind];
  
                    if (isValid(r, c, n) && grid[r] == 1) {
                        int nodeNo = row * n + col;
                        int adjNodeNo = r * n + c;
                        ds.unionBySize(nodeNo, adjNodeNo);
                    }
                }
            }
        }
  
        int mx = 0;
  
        // Find the size of the largest island by examining each water cell
        for (int row = 0; row < n; row++) {
            for (int col = 0; col < n; col++) {
                if (grid[row][col] == 1)
                    continue;
  
                Set<Integer> components = new HashSet<>();
  
                // Check neighboring land cells
                for (int ind = 0; ind < 4; ind++) {
                    int r = row + dr[ind];
                    int c = col + dc[ind];
  
                    if (isValid(r, c, n)) {
                        if (grid[r] == 1) {
                            components.add(ds.findUPar(r * n + c));
                        }
                    }
                }
  
                int sizeTotal = 0;
  
                // Calculate the total size of connected land components
                for (int it : components) {
                    sizeTotal += ds.getSize(it);
                }
  
                // Update the maximum size
                mx = Math.max(mx, sizeTotal + 1);
            }
        }
  
        // Find the maximum size among all sets
        for (int cellNo = 0; cellNo < n * n; cellNo++) {
            mx = Math.max(mx, ds.getSize(cellNo));
        }
  
        return mx;
    }
}


Time Complexity: O(N2), As we are traversing the grid of size N*N for two times and other operation will take constant time, So our complexity becomes O(N*N).
Auxiliary Space: O(N2), As we are making three arrays of size N*N.



Like Article
Suggest improvement
Share your thoughts in the comments

Similar Reads