Leetcode 200. 岛屿的个数(扩展)

1.题目描述

给定一个由 '1'(陆地)和 '0'(水)组成的的二维网格,计算岛屿的数量。一个岛被水包围,并且它是通过水平方向或垂直方向上相邻的陆地连接而成的。你可以假设网格的四个边均被水包围。

示例 1:

输入:
11110
11010
11000
00000

输出: 1

示例 2:

输入:
11000
11000
00100
00011

输出: 3

2.解题思路

算法核心框架:深度搜索+递归

    • 深度搜索:整个岛屿是一个“二维矩阵”(vector<vectro<char>>),使用深度优先搜索遍历一遍整个矩阵;
    • 递归:当扫描到'1'时,调用infect()函数,将该 ‘1’ 上下左右相邻的位置都感染为 ‘2’,递归调用infect()的结果是从起始的 ‘1’ 开始而相连的一片‘1’都被感染为 ‘2’;

3.提交代码

class Solution {
public:
     int numIslands(vector<vector<char>>& grid) {
        //行空 或 列空
        if(grid.empty() || grid[0].empty()){
            return 0;
        }
        int N = grid.size();//grid网格的列长
        int M = grid[0].size();//grid网格的行长
        int res = 0;
        //深度优先搜索
        for(int i = 0; i < N; ++i){
            for(int j = 0; j < M; ++j){
                if(grid[i][j] == '1'){
                    ++res;//只计数作为起始感染点的1的个数,即为岛屿个数
                    infect(grid,i,j,N,M);
                }
            }
        }
        return res;
    }
    
    void infect(vector<vector<char>>& grid,int i,int j,int N,int M){
        if(i<0 || i>=N || j<0 || j>= M || grid[i][j] != '1'){
            return;
        }
        grid[i][j] = '2';
        infect(grid,i+1,j,N,M);
        infect(grid,i-1,j,N,M);
        infect(grid,i,j-1,N,M);
        infect(grid,i,j+1,N,M);
    }

};

4.补充知识——并查集

并查集的结构本质是多叉树,是树的一种应用。对于并查集的具体结构,不同的教材版本有不同的定义,我这里介绍的文末所列参考资料中定义。

    • 初始时,每个单一元素是一个集合,并且各自的父指针指向自己,各个集合的“代表结点”是其自身;
    • 合并时,总是将相对较小的集合合并到大集合中,合并操作只需要将小集合的代表结点的父指针指向大集合的代表结点;
    • 查找时,两个元素的代表结点相同,表示在同一个集合。
初始时:每个元素的父指针指向自己
1->1   2->2   3->3   4->4   5->5   

合并1,2,得到set1: 2的父指针指向1
2——>1->1 

合并3,4,5,得到set2: 集合相等,可以任意选择哪个集合合并到哪个集合,这里3和4并入5
3——>5->5
4——>5->5


合并set1和set2:小集合set1的代表结点的父指针指向set2的代表结点
2——>1——>5->5
          3——>5->5
          4——>5->5


查找元素2和4是否在同一个集合:
2往上查找得到代表结点为5,4往上查找得到代表结点为5,所以,2和4在同一个集合中。

并查集是一种简单的集合表示,主要的作用有两个:

    1. 快速的,检查两个元素是否属于同一个集合,isSameSet(A,B);
    2. 快速的,将两个元素各自所在的不同集合合并在一起,Union(A,B),指的合并A所在的集合set1和B所在的集合set2;

另外,特别注意一点:并查集要求一次性获得所有的处理数据样本,不支持处理动态的数据流。

5.扩展解法——多任务并行计算

假设题目给的数据非常多,也就是所要查找的矩阵相当大,这种情况下,我们该怎么办?很明显,上面的解法只能在单机上运行,效率有上限。

因此,我们需要采用多任务处理的“并行计算”的思路(让你在面试官面前脱颖而出),将大的原始矩阵分割成多个小的矩阵,分布在多台机器上进行计算,最后合并每台机器的岛屿数量,并去重,得到最终的岛屿总数。

#问题的难点:边界信息该如何合并?

01111 | 11110
11000 | 00000
11000 | 00000
01111 | 11110
岛数1    岛数2
合并结果:岛数1 (怎么得到?)

#解决思路:需要保存的关键信息有两个:

  • (1)每个分矩阵的岛屿个数;
  • (2)记录边界点的感染中心——需要使用到并查集的结构,快速查找两个‘1’是否属于同一个集合(相同的感染中心)。

  

举例:划分为2个矩阵
矩阵1    矩阵2
01111 | 11110
01111 | 10000
00000 | 10001
01111 | 10001
00001 | 001111矩阵1的岛屿个数为2,矩阵2的岛屿个数为22)边界点的感染中心:为了易于区别不同的'1',设红、蓝、黑、绿色岛屿的感染中心(感染起始点的那个'1')的分别为A、B、C、D
0A111 | B1110
01111 | 10000
00000 | 1000D
0C111 | 10001
00001 | 00111
第1行边界的两个1,感染中心分别为A和B,不相同,岛屿数减一,并且将感染中心为B的岛屿上的1的感染中心全部置为A;
第2行边界的两个1,感染中心分别为A和A(岛屿B的感染中心在上一步更新为A了),相同,跳过;
第3行边界的两个不全为1,跳过;
第4行边界的两个1,感染中心分别为C和A,不相同,岛屿数减一,并且将感染中心为C的岛屿上的1的感染中心全部置为A;
第5行边界的两个不全为1,跳过;

岛屿总数:2+2 -1 -1 = 2

两个分矩阵的合并处理解决了,四个或八个矩阵的合并同理,只是需要重复的步骤多了。

参考资料:

1.《数据结构考研复习指导》--王道单科书 www.cskaoyan.com

2.《算法初级班视频 》--牛客网大牛 · 左程云

原文地址:https://www.cnblogs.com/paulprayer/p/9966349.html