305. Number of Islands II

A 2d grid map of m rows and n columns is initially filled with water. We may perform an addLand operation which turns the water at position (row, col) into a land. Given a list of positions to operate, count the number of islands after each addLand operation. 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.

Example:

Input: m = 3, n = 3, positions = [[0,0], [0,1], [1,2], [2,1]]
Output: [1,1,2,3]

Explanation:

Initially, the 2d grid grid is filled with water. (Assume 0 represents water and 1 represents land).

0 0 0
0 0 0
0 0 0

Operation #1: addLand(0, 0) turns the water at grid[0][0] into a land.

1 0 0
0 0 0   Number of islands = 1
0 0 0

Operation #2: addLand(0, 1) turns the water at grid[0][1] into a land.

1 1 0
0 0 0   Number of islands = 1
0 0 0

Operation #3: addLand(1, 2) turns the water at grid[1][2] into a land.

1 1 0
0 0 1   Number of islands = 2
0 0 0

Operation #4: addLand(2, 1) turns the water at grid[2][1] into a land.

1 1 0
0 0 1   Number of islands = 3
0 1 0

Follow up:

Can you do it in time complexity O(k log mn), where k is the length of the positions?

方法1: Union Find

思路:

首先确定最多只会有positions.size()块陆地。将二维数组的坐标压缩成一维,并用roots记录该点的祖先。可以进行path compression 和weighted union find改进速度

易错点:

  1. idx = x * n + y: 不是m
  2. 只有当该点是海水在当地建岛才cnt++,这一步主要防止重复建岛
  3. 上下左右guard against oob
  4. 每个pos对应一个result结果

Complexity

Time complexity : O(m×n+L) where L is the number of operations, m is the number of rows and n is the number of columns. it takes O(m×n) to initialize UnionFind, and O(L) to process positions. Note that Union operation takes essentially constant time^1 when UnionFind is implemented with both path compression and union by rank.

Space complexity : O(m×n) as required by UnionFind data structure.

朴素版union find

class Solution {
public:
    vector<int> numIslands2(int m, int n, vector<pair<int, int>>& positions) {
        vector<int> roots(m * n, -1);
        vector<int> result;
        vector<vector<int>> dirs{{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
        
        int cnt = 0;
        for (auto pos: positions){
            int id = n * pos.first + pos.second;
            if (roots[id] == -1) {
                roots[id] = id;
                // 最多有positions.size()个岛屿
                // 这里用if是因为如果在同一个地点多次建岛,不应该重复计数
                // 但是如果多次建岛,下面direction的循环不会跳过,虽然肯定已经有相同的祖先,还是会四个方向的所有岛都重新assign一遍。重复进行dfs,浪费时间。
                cnt ++;
            }
        
            // id遍历上下左右
            for (auto dir: dirs){
                int x = pos.first + dir[0];
                int y = pos.second + dir[1];
                int id_next = x * n + y;
                
                // 如果邻居点超出边界或者没有岛,跳过不做处理
                if (x < 0 || x >= m || y < 0 || y >= n || roots[id_next] < 0){
                    continue;
                }  
                // 只有当两点都为岛屿时,union
                else{        
                    int par1 = find(roots, id_next);
                    int par2 = find(roots, id);
                    // 如果不属于同一片陆地,陆地数量--
                    if (par1 != par2){
                        roots[par1] = par2;
                        cnt --;
                    }
                }
            }
            // 每次加入一块土地并union后,记录该时刻的cnt
            result.push_back(cnt);   
        }
        return result;
    }
    
    int find(vector<int> & roots, int id){
            while (roots[id] != id){
            	// 这一步只往上挪了一个祖先?但是快了很多
            	roots[id] = roots[roots[id]];
                id = roots[id];
            }
            return id;
        }
};

用weighted union find改进。
这里union_op返回bool,标示有没有发生weighted union

class Solution{
public:
    vector<int> numIslands2(int m, int n, vector<vector<int>>& positions) {
        vector<int> roots(m * n, -1);
        vector<int> size(m * n, 1);
        vector<int> result;
        
        vector<vector<int>> dirs{{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
        
        int cnt = 0;
        for (auto pos: positions){
            int id = n * pos[0] + pos[1];
            if (roots[id] == -1) {
                roots[id] = id;
                // 最多有positions.size()个岛屿
                // 这里用if是因为如果在同一个地点多次建岛,不应该重复计数
                cnt ++;
            }
        
            // id遍历上下左右
            for (auto dir: dirs){
                int x = pos[0] + dir[0];
                int y = pos[1] + dir[1];
                int id_next = x * n + y;
                
                // 如果邻居点超出边界或者没有岛,跳过不做处理
                if (x < 0 || x >= m || y < 0 || y >= n || roots[id_next] < 0){
                    continue;
                }  
                // 只有当两点都为岛屿时,union
                else{
                    
                    // 如果不属于同一片陆地,陆地数量--
                    // 这里union_op返回bool,标示有没有发生weighted union
                    if (union_op(id, id_next, roots, size)){
                        cnt --;
                    }
                }
            }
            // 每次加入一块土地并union后,记录该时刻的cnt
            result.push_back(cnt);   
        }
        return result;
    }
    
    int find_op(vector<int> & roots, int id){
            while (roots[id] != id){
                // 这一步只往上挪了一个祖先?
               //roots[id] = roots[roots[id]];
                id = roots[id];
            }
            return id;
        }
    
    bool union_op(int id, int idx, vector<int> & roots, vector<int> & size){
        int x = find_op(roots, id);
        int y = find_op(roots, idx);
        if (x != y) {
            if (size[x] <= size[y]){
                roots[x] = roots[y]; // 这里对吗
                size[y] += size[x];  // 这里对吗 对比323的模版
                return true;
            }
            else {
                roots[y] = roots[x];
                size[x] += size[y];
                return true;
            }
        }
        return false;
    }
    
};

方法2: dfs

思路:

每一次call 200题的function,每次新加入一个pos,就做一次dfs

Complexity

O(Lmn)

class Solution1 {
private:
  int numIslands(vector<vector<char>>& grid) {
    int nr = grid.size();
    if (!nr) return 0;
    int nc = grid[0].size();

    int num_islands = 0;
      
    for (int r = 0; r < nr; ++r) {
      for (int c = 0; c < nc; ++c) {
        if (grid[r][c] == '1') {
          dfs(grid, r, c);
          ++num_islands;
        }
      }
    }

    return num_islands;
  }

  void dfs(vector<vector<char>>& grid, int r, int c) {
    int nr = grid.size();
    int nc = grid[0].size();

    //grid[r][c] = '0';   // sink
    if (r<0 || r>=nr || c<0 || c>=nc || grid[r][c] != '1') return; 
    grid[r][c] = '0';   // sink  set as visited
    dfs(grid, r - 1, c);
    dfs(grid, r + 1, c);
    dfs(grid, r, c-1);
    dfs(grid, r, c+1);
  }


public:
  vector<int> numIslands2(int m, int n, vector<pair<int, int>>& positions) {
    vector<int> ans;
    vector<vector<char>> grid (m, vector<char>(n, '0'));
    for (auto pos : positions) {
      grid[pos.first][pos.second] = '1';
      ans.push_back(numIslands(grid));
    }

    return ans;
  }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值