其他分享
首页 > 其他分享> > week10_task_并查集图

week10_task_并查集图

作者:互联网

十、并查集

在这里插入图片描述


目录


来源

极客时间2021算法训练营

作者: 李煜东


1 并查集

并查集是一种树型的数据结构,用于处理一些不相交集合的合并及查询问题。
并查集的思想是用一个数组表示了整片森林(parent),树的根节点唯一标识了一个集合,我们只要找到了某个元素的的树根,就能确定它在哪个集合里。

1.1相关题目

1.1.1 547 . 省份数量

class Union():
    def __init__(self,n):
        self.fa = [i for i in range(n)]
    def find(self, x):
        if self.fa[x] == x:return x
        self.fa[x] = self.find(self.fa[x])
        return self.fa[x]
    def unionSet(self, x, y):
        x, y = self.find(x), self.find(y)
        if x != y:
            self.fa[x] = y
            
class Solution:
    def findCircleNum(self, isConnected: List[List[int]]) -> int:
        n, ans = len(isConnected), 0
        un = Union(n)

        # 合并有边的两个集合
        for i in range(n):
            for j in range(n):
                if isConnected[i][j]:
                    un.unionSet(i, j)

        # 有几个根就有几个省
        for i in range(n):
            if un.find(i) == i:
                ans += 1
        return ans

1.1.2 130 . 被围绕的区域

class Union():
    def __init__(self,n):
        self.fa = [i for i in range(n)]
        self.outside = self.fa[-1]
    def num(i, j):
        return i
    def find(self, x):
        if self.fa[x] == x:return x
        self.fa[x] = self.find(self.fa[x])
        return self.fa[x]
    def unionSet(self, x, y):
        x, y = self.find(x), self.find(y)
        if x != y:
            self.fa[x] = y

class Solution:
    def solve(self, board: List[List[str]]) -> None:
        m, n = len(board), len(board[0])
        un = Union(n * m + 1)
        ouside = un.fa[-1]

        def num(i, j):
            return i * n + j

        dr = [(0, 1), (0, -1), (1, 0), (-1, 0)]
        for i in range(m):
            for j in range(n):
                if board[i][j] == 'X':continue
                for k in dr:
                    ni = i + k[0]
                    nj = j + k[1]
                    if ni < 0 or nj < 0 or ni >= m or nj >= n:   # 若下一方向节点出界且为O 与outside相连
                        un.unionSet(num(i, j), un.outside)
                    else:
                        if board[ni][nj] == 'O':
                            un.unionSet(num(i,j), num(ni,nj))
        for i in range(m):
            for j in range(n):
                if board[i][j] == 'O' and un.find(num(i, j)) != un.find(un.outside): #与外部不相连且为O
                    board[i][j] = 'X'
        

        




2 图及相关算法

链表、树、图的关系: 链表是特殊化的树 , 树是特殊化的图

在这里插入图片描述

2.1 Bellman-Ford 算法


class Solution:
    def networkDelayTime(self, times: List[List[int]], n: int, k: int) -> int:
        dist = [1e9] * (n + 1)
        dist[k], ans = 0, 0
        for i in range(1, n):  #至多循环n-1轮
            updated = False
            for edge in times:
                x, y, z = edge
                if dist[y] > dist[x] + z: #比原先距离小则更新
                    dist[y] = dist[x] + z
                    updated = True
            if not updated:break

        for i in range(1, n + 1):
            ans = max(ans, dist[i])
        return -1 if ans == 1e9 else ans

2.2 Dijkstra 算法

  1. 初始化dist[l] = 0,其余节点的dist值为正无穷大。
  2. 找出一个未被标记的、dist[x]最小的节点x,然后标记节点x
  3. 扫描节点 x所有出边(x, y, z),若 dist[y] > dist[x] + z,则使用 dist[x] + z 更新 dist[y]
  4. 重复上述2〜3两个步骤,直到所有节点都被标记。
  1. Dijkstra – 懒惰删除 >>>> O(n^2) + m
class Solution:
    def networkDelayTime(self, times: List[List[int]], n: int, k: int) -> int:
        dist = [1e9] * (n + 1)
        dist[k], ans = 0, 0
        ver = [[] for _ in range(n + 1)]     #存点存边
        edge = [[] for _ in range(n + 1)]
        expand = [False] * (n + 1)
        for t in times:
            x, y, z = t
            ver[x].append(y)    #端点
            edge[x].append(z)   #边权


        for r in range(1 , n + 1):  #n轮
            temp = 1e9
            for i in range(1, n + 1):
                if not expand[i] and dist[i] < temp:  #没有拓展过  并且  目前最小dist[i]
                    temp = dist[i]
                    min_x = i            #从min_x出发考虑出边
            expand[min_x] = True

            for i in range(len(ver[min_x])):
                y = ver[min_x][i]
                z = edge[min_x][i]
                if dist[y] > dist[min_x] + z: 
                    dist[y] = dist[min_x] + z

        for i in range(1, n + 1):
            ans = max(ans, dist[i])
        return -1 if ans == 1e9 else ans
  1. Dijkstra – 堆 >> O(m*log(n))
from heapq import *
class Solution:
    def networkDelayTime(self, times: List[List[int]], n: int, k: int) -> int:
        dist = [1e9] * (n + 1)
        dist[k], ans = 0, 0
        ver = [[] for _ in range(n + 1)]     #存点存边
        edge = [[] for _ in range(n + 1)]
        expand = [False] * (n + 1)
        for t in times:
            x, y, z = t
            ver[x].append(y)    #端点
            edge[x].append(z)   #边权

        q = []
        heappush(q, (0, k))
        while q:
            distance, min_x = heappop(q)
            if expand[min_x]:continue  #最小已经拓展过
            expand[min_x] = True

            for i in range(len(ver[min_x])):
                y = ver[min_x][i]
                z = edge[min_x][i]
                if dist[y] > dist[min_x] + z: 
                    dist[y] = dist[min_x] + z
                    heappush(q, (dist[y], y))

        for i in range(1, n + 1):
            ans = max(ans, dist[i])
        return -1 if ans == 1e9 else ans
        

2.3 Floyd算法

1334 . 阈值距离内邻居最少的城市

class Solution:
    def findTheCity(self, n: int, edges: List[List[int]], distanceThreshold: int) -> int:
        # 存邻接矩阵d
        d = [[1e9] * n for _ in range(n)]
        for i in range(n):
            d[i][i] = 0
        for edge in edges:
            x, y, z = edge
            d[x][y] = d[y][x] = z

        # dp  --  Floyd算法
        for k in range(n):#中继点  必须先阶段
            for i in range(n):
                for j in range(n):
                    d[i][j] = min(d[i][j], d[i][k] + d[k][j])  #用中继或不用

        # 统计neighbour
        Minneighbour, ans = 1e9, 0
        for i in range(n):
            neighbour = 0
            for j in range(n):
                if i != j and d[i][j] <= distanceThreshold:
                    neighbour += 1
            if Minneighbour > neighbour or (Minneighbour == neighbour and i > ans):
                Minneighbour = neighbour
                ans = i
        return ans

2.4 Kruskal算法

  1. 否则,合并x,y所在的集合,并把z累加到答案中。
  2. 所有边扫描完成后,第4步中处理过的边就构成最小生成树。 时间复杂度为O(mlogm)

1584 . 连接所有点的最小费用

class Solution:
    def minCostConnectPoints(self, points: List[List[int]]) -> int:
        # 构造出边
        edges, n, ans = [], len(points), 0
        for i in range(n):
            for j in range(i + 1, n): #i到j 与 j 到 i相同
                edges.append([i, j, abs(points[i][0] - points[j][0]) + abs(points[i][1] - points[j][1])])
        # 边权排序
        edges.sort(key = lambda x: x[2])
        # Kruskal算法
        self.fa = []
        for i in range(n):
            self.fa.append(i)

        for edge in edges:
            x, y, z = self.find(edge[0]), self.find(edge[1]), edge[2]
            if x != y:
                self.fa[x] = y
                ans += z
        return ans
        
    def find(self, x):
        if x == self.fa[x]:
            return x
        self.fa[x] = self.find(self.fa[x])
        return self.fa[x]

作业

1 684 . 冗余连接

class Solution:
    def findRedundantConnection(self, edges: List[List[int]]) -> List[int]:
        n = len(edges)
        fa = [i for i in range(n + 1)]


        def find(x):
            if x == fa[x]:
                return x
            fa[x] = find(fa[x])
            return fa[x]

        def unionSet(x, y):
            x, y = find(x), find(y)
            if x == y:
                return True    #有环
            else:
                fa[y] = x
                return False   #目前没环

        for s, t in edges:
            if unionSet(s, t):
                return [s, t]

2 200 . 岛屿数量

class Union():
    def __init__(self,n):
        self.fa = [i for i in range(n)]
        self.outside = self.fa[-1]
    def find(self, x):
        if self.fa[x] == x:return x
        self.fa[x] = self.find(self.fa[x])
        return self.fa[x]
    def unionSet(self, x, y):
        x, y = self.find(x), self.find(y)
        if x != y:
            self.fa[x] = y

class Solution:
    def numIslands(self, grid: List[List[str]]) -> int:
        m, n, ans = len(grid), len(grid[0]), 0
        un = Union(m * n)

        def num(i, j):
            return i * n + j

        dr = [(0, 1), (0, -1), (1, 0), (-1, 0)]
        for i in range(m):
            for j in range(n):
                if grid[i][j] == '0':continue
                for k in dr:
                    ni = i + k[0]
                    nj = j + k[1]
                    if ni < 0 or nj < 0 or ni >= m or nj >= n or grid[ni][nj] == '0':
                        continue
                    un.unionSet(num(i,j), num(ni,nj))
        for i in range(m):
            for j in range(n):
                if grid[i][j] == '1' and un.find(num(i, j)) == num(i, j):
                    ans += 1
        return ans

标签:week10,task,dist,并查,self,fa,range,ans,def
来源: https://blog.csdn.net/yuliuchenyin/article/details/122580180