岛问题与并查集

124 阅读3分钟

岛问题

题目

一个矩阵中只有0和1两种值,每个位置都可以和自己的上、下、左、右四个位置相连,如果有一片1连在一起,这个部分叫做一个岛,求一个矩阵中有多少个岛?

【举例】

001010

111010

100100

000000

这个矩阵中有三个岛

Infect

从左上到右下依次遍历所有数字

当遇到0时,不处理

当遇到1时,找到一片岛屿,进入感染流程

将它的上下左右感染成2,再把它上下左右的上下左右感染成2 。。。。

直到越界或者不为1返回

当遍历到0,2时,res=1

002010

222010

200100

000000

遍历到0,4时,res=2

002020

222020

200100

000000

遍历到2,3时,res=3

002020

222020

200200

000000

一共找到三个岛

package main
​
import "fmt"
​
func countIslands(m [][]int) int {
   if m == nil || m[0] == nil {
      return 0
   }
   N := len(m)
   M := len(m[0])
   res := 0
   for i := 0; i < N; i++ {
      for j := 0; j < M; j++ {
         if m[i][j] == 1 {
            res++
            infect(m, i, j, N, M)
         }
      }
   }
   return res
}
func infect(m [][]int, i, j, N, M int) {
   if i < 0 || i >= N || j < 0 || j >= M || m[i][j] != 1 {
      return
   }
   m[i][j] = 2
​
   infect(m, i, j-1, N, M)
   infect(m, i, j+1, N, M)
   infect(m, i-1, j, N, M)
   infect(m, i+1, j, N, M)
}
func main() {
   m := [][]int{
      {0, 0, 0, 0, 0, 0, 0, 0, 0},
      {0, 1, 1, 1, 0, 1, 1, 1, 0},
      {0, 1, 1, 1, 0, 0, 0, 1, 0},
      {0, 1, 1, 0, 0, 0, 0, 0, 1}}
   fmt.Println(countIslands(m))
}

如何设计一个并行算法解决这个问题

并查集

a b c d e 都是单独的集合

{a},{b},{c},{d},{e}

判断a b是否属于同个集合,a的father是a,b的father是b,a和b不一样,则isSameSet=false

a union b,将b的father指向a

image.png

判断{a,b}与{c}是否属于同个集合 ,{a,b}的father是a,c是c,isSameSet=false

如果我们union成一个很长的链,{a,b,c,d},这时候d找father a耗时加大,可以沿途将c d的father设置成a,扁平化处理

切割矩阵

1 1 1 1 1 1

1 0 0 0 0 1

1 0 1 1 1 1

1 0 1 0 0 0

1 0 1 1 1 1

这里其实只有一个岛

image.png

左CPU算出两个岛,右CPU算出两个岛,一共四个岛

记录边界的信息属于哪个岛

image.png

(0,2) 与 (0,3)相连,并查集判断A B不属于一个集合,

四个岛减去一个岛,变成三个岛

{A,B} {C},{D}

(2,2)与(2,3)相连,并查集判断C B 不属于一个集合

3个岛减去一个岛,变成2个岛

{A,B,C}{D}

(4,2)与(4,3)相连,并查集判断C D不属于一个集合

2个岛减去一个岛,变成一个岛

返回一个岛

将任意大的矩阵任意切割成CPU数个小矩阵,每个CPU记录岛数量与边界信息即可