连通块个数 lintcode433

给定一个由0,1组成的二维数组,1为通,0不通,考虑上下左右为通的可以连通在一起,问连通块个数。
三种解法,bfs, dfs, union_find。

bfs用队列实现,为1的入队。队头出队,它上下左右如果有为1的并且没出界的依次入队,直到队列为空。进入过队列的原地置0来防止再次进队。m*n的二维数组每一个点,依次遍历,如果为1就进行bfs。
进行bfs的次数是连通块的个数,未遍历到的点可能在之前的bfs中被置0,所以此法可行。

dfs用递归实现,如果是1,原地置0,对它的上下左右是1并没出界的点进行递归调用,递归函数内对当前的点置0,递归出口是递归函数最后一行的return语句。m*n的二维数组每个点遍历一遍,1的个数是联通块的个数,此前dfs可能对未遍历的点置0,所以此法可行。

union_find,union是连接两个点,find是找根。union先分别找两个点的根,如果两个点的根不同,将第一个点的根的父亲置为第二个点的根,如果两个点的根相同不做处理。find用递归实现,如果当前点的父亲是本身直接返回当前的点,如果不是,将当前点的父亲置为当前点的父亲的根(这里递归调用find),返回当前点的父亲。总的1的个数在union时如果两个点的根不同就减1,最后的数就是联通块个数。

时间复杂度:
bfs和dfs:
使用邻接表时,BFS/DFS的时间复杂度为O(V + E);使用邻接矩阵时,BFS/DFS的时间复杂度为O(V^2),其中V代表顶点,E代表边。在本题,输入是由01组成的邻接矩阵,所以时间复杂度是顶点个数mn。
union_find:
当既没有路径压缩也没union_by_rank时是O(n),n是节点个数。
当仅有路径压缩时,O(n+f(1+log_(2+f/n) n)。
当仅有union_by_rank时, O(m
log n),m是查找次数。
当路径压缩和union_by_rank同时使用时,O(m*alpha(n)),m是查找次数,alpha(n)<=4。



class Solution:
    """
    @param grid: a boolean 2D matrix
    @return: an integer
    """
    def in_bound(self, grid, i, j):
        return 0<=i<len(grid) and 0<=j<len(grid[0])
    def bfs(self, grid, i, j):
        direction = [(0,1), (1,0), (0,-1), (-1,0)]
        grid[i][j] = 0
        queue_ = []
        queue_.append((i,j))
        while(queue_):
            x, y = queue_.pop(0)
            for dx, dy in direction:
                x_ = x+dx
                y_ = y+dy
                if self.in_bound(grid, x_, y_) and grid[x_][y_]:
                    grid[x_][y_] = 0
                    queue_.append((x_, y_))

    def numIslands(self, grid): # bfs
        # write your code here
        # union find solution
        ans = 0
        for i in range(len(grid)):
            for j in range(len(grid[0])):
                if grid[i][j]:
                    self.bfs(grid, i, j)
                    ans += 1
        return ans

        


class Solution:
    """
    @param grid: a boolean 2D matrix
    @return: an integer
    """
    def in_bound(self, grid, i, j):
        return 0<=i<len(grid) and 0<=j<len(grid[0])
    def dfs(self, grid, i, j):
        grid[i][j] = 0
        direction = [(0, 1), (1, 0), (0, -1), (-1, 0)]
        for dx, dy in direction:
            x_ = i+dx
            y_ = j+dy
            if self.in_bound(grid, x_, y_) and grid[x_][y_]:
                self.dfs(grid, x_, y_)

    def numIslands(self, grid): # dfs
        # write your code here
        # union find solution
        ans = 0
        for i in range(len(grid)):
            for j in range(len(grid[0])):
                if grid[i][j]:
                    self.dfs(grid, i, j)
                    ans += 1
        return ans

       

        

union_find:只有路径压缩。



class Solution:
    """
    @param grid: a boolean 2D matrix
    @return: an integer
    """
    def numIslands(self, grid):
        # write your code here
        # union find solution
        if not grid or not grid[0]:
            return 0
            
        self.parent = {}
        self.size = 0
        
        for i in range(len(grid)):
            for j in range(len(grid[0])):
                if grid[i][j]:
                    self.parent[(i, j)] = (i, j)
                    self.size += 1
                    
        for x in range(len(grid)):
            for y in range(len(grid[0])):
                if grid[x][y]:
                    for delta_x, delta_y in [(1, 0), (-1, 0), (0, 1), (0, -1)]:
                        x_ = x + delta_x
                        y_ = y + delta_y
                        
                        if self.is_inbound(grid, x_, y_) and grid[x_][y_]:
                            self.union((x, y), (x_, y_))
        
        return self.size
        
    def union(self, a, b):
        root_a = self.find(a)
        root_b = self.find(b)
        
        if root_a != root_b:
            self.parent[root_a] = root_b
            self.size -= 1
    
    def find(self, point):
        if point == self.parent[point]:
            return point
        
        self.parent[point] = self.find(self.parent[point])

        return self.parent[point]
        
    def is_inbound(self, grid, x_, y_):
        return 0 <= x_ < len(grid) and 0 <= y_ < len(grid[0])

union_find 的改进, 因为只要没漏掉任意两对节点的关系就可以,遍历的时候,若当前节点为1,那么只用右下走就行了,当然要考虑右下分别是否为1和分别是否越界。



class Solution:
    """
    @param grid: a boolean 2D matrix
    @return: an integer
    """
    parent = {}
    def in_bound(self, grid, i, j):
        return 0<=i<len(grid) and 0<=j<len(grid[0])
    def root(self, a):
        if a == self.parent[a]:
            return a
        self.parent[a] = self.root(self.parent[a])
        return self.parent[a]

    def union(self, a, b):
        root_a = self.root(a)
        root_b = self.root(b)

        if root_a != root_b:
            self.parent[root_b] = root_a
            self.size -= 1
        


    def numIslands(self, grid): # union_find
        # write your code here
        # union find solution
        if not grid:
            return 0
        self.size = 0
        for i in range(len(grid)):
            for j in range(len(grid[0])):
                if grid[i][j]:
                    self.size += 1
                self.parent[(i, j)] = (i, j)
        
        for i in range(len(grid)):
            for j in range(len(grid[0])):
                if grid[i][j]:
                    for dx, dy in [(0, 1), (1, 0)]:
                        x_ = i+dx
                        y_ = j+dy
                        if self.in_bound(grid, x_, y_) and grid[x_][y_]:
                            self.union((i, j), (x_, y_))
        return self.size
      

union_find 路径压缩和union_by_rank同时使用。union_by_rank操作的地方是初始化为0,union时确定父子关系的两个节点的根的rank的比较,以及确定父子关系后父亲的rank加1,共三个地方。



class Solution:
    """
    @param grid: a boolean 2D matrix
    @return: an integer
    """
    def numIslands(self, grid):
        # write your code here
        # union find solution
        if not grid or not grid[0]:
            return 0
            
        self.parent = {}
        self.rank = {}
        self.size = 0
        
        for i in range(len(grid)):
            for j in range(len(grid[0])):
                if grid[i][j]:
                    self.parent[(i, j)] = (i, j)
                    self.rank[(i, j)] = 0
                    self.size += 1
                    
        for x in range(len(grid)):
            for y in range(len(grid[0])):
                if grid[x][y]:
                    for delta_x, delta_y in [(1, 0), (-1, 0), (0, 1), (0, -1)]:
                        x_ = x + delta_x
                        y_ = y + delta_y
                        
                        if self.is_inbound(grid, x_, y_) and grid[x_][y_]:
                            self.union((x, y), (x_, y_))
        
        return self.size
        
    def union(self, a, b):
        root_a = self.find(a)
        root_b = self.find(b)
        
        if root_a != root_b:
            if self.rank[root_a]>self.rank[root_b]:
                self.parent[root_b] = root_a
            elif self.rank[root_b]>self.rank[root_a]:
                self.parent[root_a] = root_b
            else:
                self.parent[root_b] = root_a
                self.rank[root_a] += 1
            self.size -= 1
    
    def find(self, point):
        if point == self.parent[point]:
            return point
        
        self.parent[point] = self.find(self.parent[point])

        return self.parent[point]
        
    def is_inbound(self, grid, x_, y_):
        return 0 <= x_ < len(grid) and 0 <= y_ < len(grid[0])

# if __name__ == "__main__":
#     m = 5
#     a = [[1,0,1,0]for i in range(3)]
#     b = [[0,1,0,0]for i in range(m-3)]
#     a.extend(b) # a.extend(b)返回None,a 原地extend。 或者a+=b或者a=a+b
#     # print(a)
#     # a[0][0]=0
#     # print(a)
#     s = Solution()
#     print(s.numIslands(a))

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值