0

I am solving a question on LeetCode.com called Number of Islands:

Given an m x n 2D binary grid grid which represents a map of '1's (land) and '0's (water), return the number of islands. An island is surrounded by water and is formed by connecting adjacent lands horizontally or vertically. You may assume all four edges of the grid are all surrounded by water.

I know how to solve it with DFS, but I am learning and came up with the below approach:

class Solution {
public:
    vector<int> parent, sz;
    int counter;

    int find(int i) {
        if(parent[i]==i) return i;
        return parent[i]=find(parent[i]);
    }
    
    void unionf(int one, int two) {
        int p1=find(one);
        int p2=find(two);
        
        if(p1==p2) return;
        if(sz[one]<sz[two]) {
            parent[one]=two;
            sz[two]+=sz[one];
        } else {
            parent[two]=one;
            sz[one]+=sz[two];
        }
        counter--;
    }
    
    int numIslands(vector<vector<char>>& grid) {
        int m=grid.size(), n=grid[0].size();
        parent.resize(m*n);
        iota(begin(parent),end(parent),0);

        sz.resize(m*n,1);
        
        counter=0;
        for(int i=0; i<m; i++) {
            for(int j=0; j<n; j++) {
                if(grid[i][j]=='0') {
                    continue;
                }
                
                //grid[i][j]=='1'; an island
                counter++;
                int idx=i*n+j;
                //traverse all 4 neighbors
                if(i+1<m && grid[i+1][j]=='1') unionf(idx,(i+1)*n+j);
                if(i-1>=0 && grid[i-1][j]=='1') unionf(idx, (i-1)*n+j);
                if(j+1<n && grid[i][j+1]=='1') unionf(idx, i*n+j+1);
                if(j-1>=0 && grid[i][j-1]=='1') unionf(idx, i*n+j-1);
            }
        }

        return counter;
    }
};

It produces right answers on the sample inputs, but wrong answer for [["1","1","1"],["0","1","0"],["1","1","1"]].

At a high level, my logic is that whenever I encounter an island (1), I increment the counter and call unionf() and try to unify it with its neighbors. If such a unification is possible, I decrement counter in unionf(), since it is linked to its parent island (a connected component) and not a new island.

Could someone please point out what I am missing in my logic? Thanks!

Someone
  • 611
  • 4
  • 13
  • I won't try to fill in the missing pieces and reproduce the error, but it looks as you can decrement the counter more than once, for an acre of land with more than one dry neighbor. – Beta Jan 12 '22 at 04:09
  • @Beta, thanks for your comment; but I don't think that's true. "An acre of land with more than one dry neighbor" ends up in the same connected component (with the same `parent`/representative) and due to `if (p1 == p2) return;` we take an early exit without decrementing the `counter`. Do you agree? – Someone Jan 13 '22 at 01:40

1 Answers1

0

Add some debug print shows some issues in union: Demo.

Changing to:

void unionf(int one, int two) {
    int p1=find(one);
    int p2=find(two);
    
    if (p1 == p2) return;
    if (sz[p1] < sz[p2]) {
        parent[p1] = p2;
        sz[p2] += sz[p1];
    } else {
        parent[p2] = p1;
        sz[p1] += sz[p2];
    }
    std::cout << "union:" << one << " and " << two
              << "(p1: " << p1 << ", p2: " << p2 << ")" << std::endl;
    counter--;
}

fix the issue (not sure about island size though).

Demo

Jarod42
  • 203,559
  • 14
  • 181
  • 302
  • Ah, I see. I was not actually updating the _representatives_ of the connected components. Also, why does @Beta's comment above not hold true? I mean how do we end up _not_ "decrement(ing) the counter more than once, for an acre of land with more than one dry neighbor."? I guess it is because since they are connected, their parents are equal and we take an early exit with: `if (p1 == p2) return;`. Am I right? – Someone Jan 13 '22 at 01:35
  • You decrement only on join, so it is ok (as long as *id* of the island is correct). – Jarod42 Jan 13 '22 at 08:54