海岛问题

通过这个问题把BFS、DFS、并查集的一些思路和程序主体模板进行一下总结。

其中BFS和DFS属于最基本最容易直接观察的,并查集属于最容易理解的。本题来自LeetCode200.岛屿数量;思路搬运自题解区第二条“liweiwei1419”大神,代码部分参考自题解区第三条“powcai”大神。

作者:liweiwei1419
链接:https://leetcode-cn.com/problems/number-of-islands/solution/dfs-bfs-bing-cha-ji-python-dai-ma-java-dai-ma-by-l/
作者:powcai
链接:https://leetcode-cn.com/problems/number-of-islands/solution/dfs-bfs-bing-cha-ji-chao-ji-qing-xi-by-powcai/

1.DFS
原题解作者引出了一个非常形象的解释,就是洪水,或者感染,我们对整片区域进行遍历,如果找到了一块陆地,先计数加1,代表我们已经发现了一块新大陆,然后我们找到与这块大陆相连的区域,并将之前发现的大陆给淹没,然后再对新发现的大陆进行相同的操作直至没有新大陆被发现了,代表我们已经处理完一整块海岛了,最后的计数就是题中所要求的的海岛数目,而我们所说的这个“洪水”或者“感染”就是一个DFS深度遍历的过程。

按照上面的思路,我们先把感染的函数,也就是DFS的模板给写出来。

def dfs(i, j):
            grid[i][j] = "0"                                  #先将当前大陆淹没
            for x, y in [[-1, 0], [1, 0], [0, -1], [0, 1]]:   #然后检查它的四周
                tmp_i = i + x
                tmp_j = j + y
                if 0 <= tmp_i < row and 0 <= tmp_j < col and grid[tmp_i][tmp_j] == "1":   
                    #如果它的四周也有大陆,则对它的四周进行相同操作
                    dfs(tmp_i, tmp_j)

然后再对整片区域进行遍历即可,因为我们检查到一个“1”之后会把与它同为海岛的所有区域全部置“0”,所以我们每发现一次1都属于独立的新大陆。

整段代码如下:

def numIslands(self, grid: List[List[str]]) -> int:
        if not grid:
            return 0
        row = len(grid)
        col = len(grid[0])
        
        def dfs(i,j):
            grid[i][j] = '0'
            for r,c in[(i-1,j),(i,j+1),(i+1,j),(i,j-1)]:
                if 0<=r<row and 0<=c<col and grid[r][c] == '1':
                    dfs(r,c)
                    
        res = 0
        for i in range(row):
            for j in range(col):
                if grid[i][j] == '1':
                    dfs(i,j)
                    res += 1
                    
        return res

2.BFS
广度遍历就是需要借助一个队列来完成相关功能,用一个集合来存储所有被遍历过的点,那么当我们发现一个点为“1”且不在集合中,我们就可以启动广度遍历,具体来说就是检查这个点四周的所有点,如果与它相连接的点未被遍历,则加入到队列中待遍历,且加入集合seen中被标记为已遍历状态。

BFS代码如下

def bfs(i,j):
            seen.add((i,j))
            queue.append([i,j])
            while queue:
                node = queue.pop(0)
                nr,nc = node[0],node[1]
                for r,c in [(nr-1,nc),(nr,nc+1),(nr+1,nc),(nr,nc-1)]:
                    if 0<=r<row and 0<=c<col and grid[r][c] == '1' and (r,c) not in seen:
                        seen.add((r,c))
                        queue.append([r,c])

整体代码如下,BFS是我自己写的,又臭又长QAQ......

def numIslands(self, grid: List[List[str]]) -> int:
        if not grid:
            return 0
        row = len(grid)
        col = len(grid[0])
        
        seen = set()
        queue = []
        
        
        def bfs(i,j):
            seen.add((i,j))
            queue.append([i,j])
            while queue:
                node = queue.pop(0)
                nr,nc = node[0],node[1]
                for r,c in [(nr-1,nc),(nr,nc+1),(nr+1,nc),(nr,nc-1)]:
                    if 0<=r<row and 0<=c<col and grid[r][c] == '1' and (r,c) not in seen:
                        seen.add((r,c))
                        queue.append([r,c])
        
        res = 0
        for i in range(row):
            for j in range(col):
                if grid[i][j] == '1' and (i,j) not in seen:
                    bfs(i,j)
                    res += 1
        
        return res

3.并查集
并查集的思路我之前的一篇文章里面已经写了,主要有两个操作,一个是寻找最原始的祖先结点,一个是连接结点的操作,只有两个节点的原始祖先结点不相同时才连接,否则的话它们本身就已经属于同一个圈子里面了。

寻找祖先结点函数

def find_fa(node):
            temp = node
            #只要这个结点不是原始结点就一直往上搜寻
            while parent[temp] != -1:
                temp = parent[temp]
            return temp

连接操作

def connect(node1,node2):
            node1 = find_pa(node1)
            node2 = find_pa(node2)
            #对于无向图,把谁当做谁的父节点都无所谓
            if node1 != node2:
                parent[node1] = node2

用并查集做处理的话,就只需要检查每个点的右边和下面然后决定是否连接即可。

记住要把延伸出来的“1”结点挂在原结点上,这样比较好理解
例如(i,j)有一个结点(i,j+1)
我们应该让(i,j+1)的父节点等于(i,j)

parent[i*col+j+1] = i*col+j

然后还应注意把所有的水域都连接到一个虚拟的节点上,即让水域的父节点等于-2。
最后统计parent数组中-1的个数即可
这一块我也是按照自己习惯的方式写了下,速度比DFS和BFS都稍微快一点。

def numIslands(self, grid: List[List[str]]) -> int:
        if not grid:
            return 0
        
        row = len(grid)
        col = len(grid[0])
        parent = [-1]*row*col
        
        def find_fa(i,j):
            node = i*col + j 
            while parent[node] != -1:
                node = parent[node]
            return node
        
        def connect(i1,j1,i2,j2):
            node1 = find_fa(i1,j1)
            node2 = find_fa(i2,j2)
            if node1 != node2:
                parent[node2] = node1
        
        for i in range(row):
            for j in range(col):
                if grid[i][j] == '1':
                    for r,c in [(i,j+1),(i+1,j)]:
                        if 0<=r<row and 0<=c<col and grid[r][c] == '1':
                            connect(i,j,r,c)
                elif grid[i][j] == '0':
                    parent[i*col+j] = -2
        
        res = 0
        for item in parent:
            if item == -1:
                res += 1
        
        return res
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,258评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,335评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,225评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,126评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,140评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,098评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,018评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,857评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,298评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,518评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,678评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,400评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,993评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,638评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,801评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,661评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,558评论 2 352