leetcode-并查集

【算法】并查集(Disjoint Set)[共3讲]_哔哩哔哩_bilibili

leetcode-广度优先遍历/图/并查集_MaYingColdPlay的博客-CSDN博客

python实现一个简单的并查集 - SegmentFault 思否

并查集板子看 找出知道秘密的所有专家

并查集

python实现各种常用算法之数据结构(7) - Hadoop_Spark的个人空间 - OSCHINA - 中文开源技术交流社区

力扣

路径压缩和按rank合并

Java

力扣

// UnionFind.class
public class UnionFind {
    int root[];
    // 添加了 rank 数组来记录每个顶点的高度,也就是每个顶点的「秩」
    int rank[];

    public UnionFind(int size) {
        root = new int[size];
        rank = new int[size];
        for (int i = 0; i < size; i++) {
            root[i] = i;
            rank[i] = 1; // 一开始每个顶点的初始「秩」为1,因为它们只有自己本身的一个顶点。
        }
    }

		// 此处的 find 函数与路径压优化缩版本的 find 函数一样。
    public int find(int x) {
        if (x == root[x]) {
            return x;
        }
        return root[x] = find(root[x]);
    }

		// 按秩合并优化的 union 函数
    public void union(int x, int y) {
        int rootX = find(x);
        int rootY = find(y);
        if (rootX != rootY) {
            if (rank[rootX] > rank[rootY]) {
                root[rootY] = rootX;
            } else if (rank[rootX] < rank[rootY]) {
                root[rootX] = rootY;
            } else {
                root[rootY] = rootX;
                rank[rootX] += 1;
            }
        }
    };

    public boolean connected(int x, int y) {
        return find(x) == find(y);
    }
}

// App.java
// 测试样例
public class App {
    public static void main(String[] args) throws Exception {
        UnionFind uf = new UnionFind(10);
        // 1-2-5-6-7 3-8-9 4
        uf.union(1, 2);
        uf.union(2, 5);
        uf.union(5, 6);
        uf.union(6, 7);
        uf.union(3, 8);
        uf.union(8, 9);
        System.out.println(uf.connected(1, 5)); // true
        System.out.println(uf.connected(5, 7)); // true
        System.out.println(uf.connected(4, 9)); // false
        // 1-2-5-6-7 3-8-9-4
        uf.union(9, 4);
        System.out.println(uf.connected(4, 9)); // true
    }
}

作者:爱学习的饲养员
链接:https://leetcode-cn.com/leetbook/read/graph/r3jbih/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

python

class union_find(object):
    def __init__(self, n):
        self.root=[i for i in range(n)]
        self.rank=[i for i in range(n)]

    def find(self, x):
        if x == self.root[x]:
            return x
        self.root[x] = self.find(self.root[x])
        return self.root[x]

    def union(self, x,y):
        rootX = self.find(x)
        rootY = self.find(y)
        if rootX!=rootY:
            if self.rank[rootX] > self.rank[rootY]:
                self.root[rootY] = rootX
            elif self.rank[rootX] < self.rank[rootY]:
                self.root[rootX] = rootY
            else:
                self.root[rootY] = rootX
                self.rank[rootX] += 1



if __name__ == '__main__':
    union_find = union_find(5)

547 省份数量

力扣

并查集模版 

class UnionFind:
    def __init__(self):
        """
        记录每个节点的父节点
        """
        self.father = {}
    
    def find(self,x):
        """
        查找根节点
        路径压缩
        """
        root = x

        while self.father[root] != None:
            root = self.father[root]

        # 路径压缩
        while x != root:
            original_father = self.father[x]
            self.father[x] = root
            x = original_father
         
        return root
    
    def merge(self,x,y,val):
        """
        合并两个节点
        """
        root_x,root_y = self.find(x),self.find(y)
        
        if root_x != root_y:
            self.father[root_x] = root_y

    def is_connected(self,x,y):
        """
        判断两节点是否相连
        """
        return self.find(x) == self.find(y)
    
    def add(self,x):
        """
        添加新节点
        """
        if x not in self.father:
            self.father[x] = None

作者:musiala
链接:https://leetcode-cn.com/problems/number-of-provinces/solution/python-duo-tu-xiang-jie-bing-cha-ji-by-m-vjdr/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

本题题解

class union_find(object):
    def __init__(self, n):
        self.root=[i for i in range(n)]
        self.rank=[i for i in range(n)]
        self.size=n

    def find(self, x):
        if x == self.root[x]:
            return x
        self.root[x] = self.find(self.root[x])
        return self.root[x]

    def union(self, x,y):
        rootX = self.find(x)
        rootY = self.find(y)
        if rootX != rootY:
            # self.root[rootX] = rootY
            if self.rank[rootX] > self.rank[rootY]:
                self.root[rootY] = rootX
            elif self.rank[rootX] < self.rank[rootY]:
                self.root[rootX] = rootY
            else:
                self.root[rootY] = rootX
                self.rank[rootY] += 1
            self.size=self.size-1

class Solution(object):
    def findCircleNum(self, isConnected):
        """
        :type isConnected: List[List[int]]
        :rtype: int
        """
        n = len(isConnected)
        uf = union_find(n)
        for i in range(n):
            for j in range(i+1,n):
                if isConnected[i][j]==1:
                    print(i,j)
                    uf.union(i,j)
        return uf.size

class UnionFind:
    def __init__(self):
        self.father = {}
        # 额外记录集合的数量
        self.num_of_sets = 0
    
    def find(self,x):
        root = x
        
        while self.father[root] != None:
            root = self.father[root]
        #有没有都可以
        # while x != root:
        #     original_father = self.father[x]
        #     self.father[x] = root
        #     x = original_father
        
        return root
    
    def merge(self,x,y):
        root_x,root_y = self.find(x),self.find(y)
        
        if root_x != root_y:
            self.father[root_x] = root_y
            # 集合的数量-1
            self.num_of_sets -= 1
    
    def add(self,x):
        if x not in self.father:
            self.father[x] = None
            # 集合的数量+1
            self.num_of_sets += 1

class Solution:
    def findCircleNum(self, M: List[List[int]]) -> int:
        uf = UnionFind()
        for i in range(len(M)):
            uf.add(i)
            for j in range(i):
                if M[i][j]:
                    uf.merge(i,j)
        
        return uf.num_of_sets

dfs

 

class Solution {
    public int findCircleNum(int[][] isConnected) {
        // int[][] isConnected 是无向图的邻接矩阵,n 为无向图的顶点数量
        int n = isConnected.length;
        // 定义 boolean 数组标识顶点是否被访问
        boolean[] visited = new boolean[n];
        // 定义 cnt 来累计遍历过的连通域的数量
        int cnt = 0;
        for (int i = 0; i < n; i++) {
            // 若当前顶点 i 未被访问,说明又是一个新的连通域,则遍历新的连通域且cnt+=1.
            if (!visited[i]) { 
                cnt++;
                dfs(i, isConnected, visited);
            }
        }
        return cnt;
    }

    private void dfs(int i, int[][] isConnected, boolean[] visited) {
        // 对当前顶点 i 进行访问标记
        visited[i] = true;
        
        // 继续遍历与顶点 i 相邻的顶点(使用 visited 数组防止重复访问)
        for (int j = 0; j < isConnected.length; j++) {
            if (isConnected[i][j] == 1 && !visited[j]) {
                dfs(j, isConnected, visited);
            }
        }
    }
}

作者:sweetiee
链接:https://leetcode-cn.com/problems/number-of-provinces/solution/dfs-bfs-bing-cha-ji-3-chong-fang-fa-ji-s-edkl/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

bfs

class Solution {
    public int findCircleNum(int[][] isConnected) {
        // int[][] isConnected 是无向图的邻接矩阵,n 为无向图的顶点数量
        int n = isConnected.length;
        // 定义 boolean 数组标识顶点是否被访问
        boolean[] visited = new boolean[n];
        
        // 定义 cnt 来累计遍历过的连通域的数量
        int cnt = 0;  
        Queue<Integer> queue = new LinkedList<>();
        for (int i = 0; i < n; i++) {
            // 若当前顶点 i 未被访问,说明又是一个新的连通域,则bfs新的连通域且cnt+=1.
            if (!visited[i]) {
                cnt++;
                queue.offer(i);
                visited[i] = true;
                while (!queue.isEmpty()) {
                    int v = queue.poll();
                    for (int w = 0; w < n; w++) {
                        if (isConnected[v][w] == 1 && !visited[w]) {
                            visited[w] = true;
                            queue.offer(w);
                        }
                    }
                }
            }
        }
        return cnt;
    }
} 

作者:sweetiee
链接:https://leetcode-cn.com/problems/number-of-provinces/solution/dfs-bfs-bing-cha-ji-3-chong-fang-fa-ji-s-edkl/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

5941 找出知道秘密的所有专家

class UnionFind:
    def __init__(self, n):
        self.count = n
        #存储父节点,初始化为第i个节点的父节点为i
        self.parent = [i for i in range(n)]
        #存储每个节点的高度(树的深度)
        self.rank = [0] * n
    #找根节点.当parent[i] != i时,说明它的父节点不是自己,即它还有父节点,就不是根节点
    def find(self, i):
        if self.parent[i] != i:
            self.parent[i] = self.find(self.parent[i])
        return self.parent[i]
 
    def union(self, x, y):
        rootx = self.find(x)
        rooty = self.find(y)
        #如果根节点不相等,说明需要合并
        if rootx != rooty:
            if self.rank[rootx] < self.rank[rooty]:
                rootx, rooty = rooty, rootx
            #合并到rank较大的根节点上
            self.parent[rooty] = rootx
            #如果两个根节点的rank一样,合并之后,高度会增加一
            if self.rank[rootx] == self.rank[rooty]:
                self.rank[rootx] += 1
            self.count -= 1
 
    def isolate(self, x):
        #父节点的值设置为初始状态
        self.parent[x] = x
        self.rank[x] = 0
 
 
class Solution:
    def findAllPeople(self, n: int, meetings: List[List[int]], firstPerson: int) -> List[int]:
 
        meetings.sort(key=lambda x: x[2])
 
        uf = UnionFind(n)
        uf.union(0, firstPerson)
 
        for _, members in groupby(meetings, key=lambda x: x[2]):
            members = list(members)
 
            # 连接同一时间开会的专家
            people = set()
            for x, y, _ in members:
                uf.union(x, y)
                people.add(x)
                people.add(y)
 
            # 开完会后, 孤立所有没知道秘密的专家。根节点不是0节点的
            for person in people:
                if uf.find(person) != uf.find(0):
                    uf.isolate(person)
 
        ans = []
        for i in range(n):
            if uf.find(i) == uf.find(0):
                ans.append(i)
        return ans

bfs

思路:

1.同一时刻的一起遍历。在遍历过程中用一个列表secret来记录当前时刻的人是否有秘密

2.在遍历的时候,每次选当前时刻有秘密的人,和他的pair对,把当前时刻有秘密的人的pair对也置为有秘密

class Solution:
    def findAllPeople(self, n: int, meetings: List[List[int]], firstPerson: int) -> List[int]:
        m = len(meetings)
        meetings.sort(key=lambda x: x[2])

        secret = [False] * n
        secret[0] = secret[firstPerson] = True

        i = 0
        while i < m:
            # meetings[i .. j] 为同一时间
            j = i
            while j + 1 < m and meetings[j + 1][2] == meetings[i][2]:
                j += 1

            vertices = set()
            edges = defaultdict(list)
            for k in range(i, j + 1):
                x, y = meetings[k][0], meetings[k][1]
                vertices.update([x, y])
                edges[x].append(y)
                edges[y].append(x)
            
            q = deque([u for u in vertices if secret[u]])
            while q:
                u = q.popleft()
                for v in edges[u]:
                    if not secret[v]:
                        secret[v] = True
                        q.append(v)
            
            i = j + 1
        
        ans = [i for i in range(n) if secret[i]]
        return ans

作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/find-all-people-with-secret/solution/zhao-chu-zhi-xiao-mi-mi-de-suo-you-zhuan-fzxf/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

2076 朋友圈

力扣

并查集硬模拟

class UnionFind:
    def __init__(self, n):
        self.n = n
        self.parent = [x for x in range(n)]
        self.size = [1 for x in range(n)]
    
    def Find(self, x: int) -> int:
        if self.parent[x] != x:
            self.parent[x] = self.Find(self.parent[x])
        return self.parent[x]
    
    def Union(self, x: int, y: int) -> bool:
        root_x = self.Find(x)
        root_y = self.Find(y)
        if root_x == root_y:
            return False
        
        if self.size[root_x] > self.size[root_y]:
            root_x, root_y = root_y, root_x
        self.parent[root_x] = root_y
        self.size[root_y] += self.size[root_x]
        return True
    
    def connected(self, x: int, y: int) -> bool:
        return self.Find(x) == self.Find(y)

    
class Solution:
    def friendRequests(self, n: int, restrictions: List[List[int]], requests: List[List[int]]) -> List[bool]:
        UF = UnionFind(n)
        
        res = [False for _ in range(len(requests))]
        
        for ri in range(len(requests)):
            a, b = requests[ri]
            
            root_a = UF.Find(a)
            root_b = UF.Find(b)
            flag = True
            for x, y in restrictions:
                root_x = UF.Find(x)
                root_y = UF.Find(y)
                if (root_a == root_x and root_b == root_y) or (root_a == root_y and root_b == root_x):
                    flag = False
                    break
            if flag:
                res[ri] = True
                UF.Union(a, b)
            else:
                res[ri] = False
                
        return res

作者:Hanxin_Hanxin
链接:https://leetcode-cn.com/problems/process-restricted-friend-requests/solution/cpython3java-1bing-cha-ji-ying-mo-ni-by-l2na8/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

2076. 处理含限制条件的好友请求

 

2157. 字符串分组

状态压缩+并查集

力扣

class union_find(object):
    def __init__(self,n):
        #注意这里的初始值。后续用UF.parent[x] == x来找根节点。因为有重复值,所以得用-1赋值
        self.parent = [-1 for i in range(n)]
        self.rank = [1 for i in range(n)]
    def find(self,x):
        if self.parent[x] != x:
            self.parent[x] = self.find(self.parent[x])
        return self.parent[x]
    def union(self,x,y):
        rootx = self.find(x)
        rooty = self.find(y)
        if rootx != rooty:
            if self.rank[rootx] < self.rank[rooty]:
                rootx, rooty = rooty, rootx
            #合并到rank较大的根节点上,root_x比较大,合并到root_x上
            #利用rank来计算集合的大小
            self.parent[rooty] = rootx
            self.rank[rootx] +=self.rank[rooty]
            # #如果两个根节点的rank一样,合并之后,高度会增加一
            # if self.rank[rootx] == self.rank[rooty]:
            #     self.rank[rootx] += 1
class Solution(object):
    def groupStrings(self, words):
        """
        :type words: List[str]
        :rtype: List[int]
        """
        #把words转化为二进制。如果string1和string2在一个集合里,就union
        #难点在于如何判断string1和string2在一个集合里:可用二进制枚举的方法
        # #枚举masks
        def get_neightbor(state):
            #添加,删除,替换
            neighbors = []
            for i in range(26):
                neighbors.append(state ^ (1 << i))
            for i in range(26):
                if state & (1 << i):
                    for j in range(26):
                        if not (state & (1 << j)):
                            neighbors.append(state ^ (1 << i) ^ (1 << j))
            return neighbors
   
        n = len(words)
        UF = union_find(n)
        #例子里有重复值,例如输入["web","a","te","hsx","v","k","a","roh"]
        word_ID =  {}
        word_count = collections.defaultdict(int)
        #-------- 状态压缩
        for i, word in enumerate(words):
            state = 0
            for c in word:
                state |= 1 << (ord(c) - ord('a'))
            word_ID[state] = i
            word_count[state] += 1
        
        for state in word_ID:
            ID = word_ID[state]
            UF.parent[ID] = ID
            UF.rank[ID] = word_count[state]
        
        for state in word_ID:
            ID = word_ID[state]
            neighbors = get_neightbor(state)
            for neighbor in neighbors:
                if neighbor in word_ID:
                    UF.union(ID,word_ID[neighbor])
        part = 0
        max_ = 0
        for x in range(n):
            if UF.parent[x] == x:
                part += 1
                max_ = max(max_, UF.rank[x])
        return [part, max_]

6106. 统计无向图中无法互相到达点对数

并查集+数学 

class union_find(object):
    def __init__(self,n):
        #注意这里的初始值。后续用UF.parent[x] == x来找根节点。因为有重复值,所以得用-1赋值
        self.parent = [i for i in range(n)]
        self.rank = [1 for i in range(n)]
    def find(self,x):
        if self.parent[x] != x:
            self.parent[x] = self.find(self.parent[x])
        return self.parent[x]
    def union(self,x,y):
        rootx = self.find(x)
        rooty = self.find(y)
        if rootx != rooty:
            if self.rank[rootx] < self.rank[rooty]:
                rootx, rooty = rooty, rootx
            #合并到rank较大的根节点上,root_x比较大,合并到root_x上
            #利用rank来计算集合的大小
            self.parent[rooty] = rootx
            self.rank[rootx] +=self.rank[rooty]
            # #如果两个根节点的rank一样,合并之后,高度会增加一
            # if self.rank[rootx] == self.rank[rooty]:
            #     self.rank[rootx] += 1

class Solution(object):
    def countPairs(self, n, edges):
        """
        :type n: int
        :type edges: List[List[int]]
        :rtype: int
        """
        if n == 1:
            return 0
        UF = union_find(n)
        for i in range(len(edges)):
            UF.union(edges[i][0], edges[i][1])
        # print(UF.parent)
        # print(UF.rank)
        part = 0
        tmp = []
        for x in range(n):
            if UF.parent[x] == x:
                part += 1
                this_size = UF.rank[x]
                tmp.append(this_size)
        # ans = 0
        # for i in range(len(tmp)):
        #     for j in range(i+1,len(tmp)):
        #         ans += tmp[i]*tmp[j]
        ans = 0
        for i in range(len(tmp)):
             ans += tmp[i] * (n - tmp[i])
        return ans/2
       

6159. 删除操作后的最大子段和

 

并查集变式

class Solution(object):
    def maximumSegmentSum(self, nums, removeQueries):
        """
        :type nums: List[int]
        :type removeQueries: List[int]
        :rtype: List[int]
        """
         #从后往前并查集。其实周赛的时候有想过从后往前做,但是没想到可以用并查集

        #往右合并,root初始化为nums
        n = len(nums)
        fa = [i for i in range(n+1)]

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

        ans = [0] * n
        sum = [0] * (n + 1)
        for i in range(n - 1, 0, -1):
            x = removeQueries[i]
            sum[x] = nums[x]
            if sum[x + 1]:  # 如果前面有
                to = find(x + 1)
                #往右连通
                fa[to] = x
                sum[x] += sum[to]
            if sum[x - 1]:  # 如果后面有
                to = find(x - 1)
                # 往右连通
                fa[to] = x
                sum[x] += sum[to]
            ans[i - 1] = max(ans[i], sum[x])
        return ans

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值