14 并查集

  • 基本概念

    • 并查集是一种树型的数据结构,用于处理一些不交集的合并及查询问题
    • 示意图
  • 常用方法
    • Find:确定元素属于哪一个子集。可以用来确定两个元素是否属于同一子集
    • Union: 将两个子集合并成同一个集合
    • 示意图
  • 代码实现(伪代码)

function MakeSet(x)

x.parent := x

function Find(x)

if x.parent == x

return x

else

return Find(x.parent)

function Union(x, y)

xRoot := Find(x)

yRoot := Find(y)

xRoot.parent := yRoot

  • 优化

    • rank

      • 示意图

      • 伪代码

function MakeSet(x)

x.parent := x

x.rank := 0

function Union(x, y)

xRoot := Find(x)

yRoot := Find(y)

if xRoot == yRoot

return

if xRoot.rank < yRoot.rank

xRoot.parent := yRoot

else if xRoot.rank > yRoot.rank

yRoot.parent := xRoot

else

yRoot.parent := xRoot

xRoot.rank := xRoot.rank + 1

  • 路径压缩

    • 示意图

    • 伪代码

function MakeSet(x)

x.parent := x

function Find(x)

root = x

while root != root.parent

root := root.parent

while x != x.parent

tmp = x.parent

x.parent = root

x = tmp

return root

  • Python 实现 -- 包含两种优化

class UnionUF(object):

def __init__(self, n):

self.parent = [-1] * n

self.rank = [0] * n

for i in range(n):

self.parent[i] = i

def find(self, i):

root = i

while root != self.parent[root]:

root = self.parent[root]

# 路径压缩

while i != self.parent[i]:

i, self.parent[i] = self.parent[i], root

return root

def union(self, x, y):

rootx = self.find(x)

rooty = self.find(y)

if rootx == rooty:

return 0# 两者属于同一集合,没有发生合并操作

if self.rank[rootx] > self.rank[rooty]:

self.parent[rooty] = rootx

elif self.rank[rootx] < self.rank[rooty]:

self.parent[rootx] = rooty

else:

self.parent[rootx] = rooty

self.rank[rooty] += 1

return 1# 发生合并操作


class UnionUF(object):

def __init__(self, n):

self.parent = [-1] * n

self.rank = [0] * n

for i in range(n):

self.parent[i] = i

def find(self, i):

root = i

while root != self.parent[root]:

root = self.parent[root]

# 路径压缩

while i != self.parent[i]:

i, self.parent[i] = self.parent[i], root

return root

def union(self, x, y):

rootx = self.find(x)

rooty = self.find(y)

if rootx == rooty:

return 0# 两者属于同一集合,没有发生合并操作

if self.rank[rootx] > self.rank[rooty]:

self.parent[rooty] = rootx

elif self.rank[rootx] < self.rank[rooty]:

self.parent[rootx] = rooty

else:

self.parent[rootx] = rooty

self.rank[rooty] += 1

return 1# 发生合并操作

class Solution:

def numIslands(self, grid: List[List[str]]) -> int:

if not grid:

return 0

m, n = len(grid), len(grid[0])

union = UnionUF(m*n)

res = 0

dx, dy = [-1, 1, 0, 0], [0, 0, -1, 1]

for i in range(m):

for j in range(n):

if grid[i][j] == "1":

res += 1

for count in range(len(dx)):

ni, nj = i + dx[count], j + dy[count]

if 0 <= ni < m and 0 <= nj < n and grid[ni][nj] == "1":

res -= union.union(i*n + j, ni*n + nj)

return res


class UnionUF(object):

def __init__(self, n):

self.parent = [-1] * n

self.rank = [0] * n

for i in range(n):

self.parent[i] = i

def find(self, i):

root = i

while root != self.parent[root]:

root = self.parent[root]

# 路径压缩

while i != self.parent[i]:

i, self.parent[i] = self.parent[i], root

return root

def union(self, x, y):

rootx = self.find(x)

rooty = self.find(y)

if rootx == rooty:

return 0# 两者属于同一集合,没有发生合并操作

if self.rank[rootx] > self.rank[rooty]:

self.parent[rooty] = rootx

elif self.rank[rootx] < self.rank[rooty]:

self.parent[rootx] = rooty

else:

self.parent[rootx] = rooty

self.rank[rooty] += 1

return 1# 发生合并操作

class Solution:

def findCircleNum(self, M: List[List[int]]) -> int:

if not M:

return 0

m = len(M)

union = UnionUF(m)

res = 0

for i in range(m):

res += 1

for count in range(m):

if i == count:

continue

if M[i][count] == 1:

res -= union.union(i, count)

return res

原文地址:https://www.cnblogs.com/lijunjie9502/p/10989224.html

时间: 2024-10-27 11:39:12

14 并查集的相关文章

[hiho 14]并查集

题目描述 并查集,顾名思义有两个操作:合并和查询. 并就是把两个集合合并到一起. 查就是查询两个节点是否属于同一个集合. 每个节点有一个父节点.一个集合内存在一个唯一的根,判断根的条件就是节点的父节点是不是该节点本身. 合并操作就是把一个集合的根接到另一个集合的根上. 而查询操作就是找两个对象是否有同一个根. 查询过程中可以顺便进行路径压缩以优化后续查询:即让查找路径上所有节点的父节点直接等于根节点. #include <iostream> #include <algorithm>

Educational Codeforces Round 14 D. Swaps in Permutation (并查集orDFS)

题目链接:http://codeforces.com/problemset/problem/691/D 给你n个数,各不相同,范围是1到n.然后是m行数a和b,表示下标为a的数和下标为b的数可以交换无数次.问你最后字典序最大的数列是什么. 将下面的a和b用并查集联系起来存到祖节点对应的数组中,然后从大到小排序数组,最后依次按照父节点的数组中的顺序输出. 也可以用dfs的方法解(略麻烦),形成一个环路的就在一个数组中... 1 //并查集 2 #include <bits/stdc++.h> 3

【二分图】【并查集】XVII Open Cup named after E.V. Pankratiev Stage 14, Grand Prix of Tatarstan, Sunday, April 2, 2017 Problem L. Canonical duel

给你一个网格(n<=2000,m<=2000),有一些炸弹,你可以选择一个空的位置,再放一个炸弹并将其引爆,一个炸弹爆炸后,其所在行和列的所有炸弹都会爆炸,连锁反应. 问你所能引爆的最多炸弹数. 转化成: 将行列当成点,炸弹当成边,然后你可以给这个二分图加1条边,问你最大的连通块的边的数量. 可以通过枚举所有可以建的边,通过并查集来尝试更新答案.由于一条边必然会让总度数+2,所以一个连通块的边数是所有点的度数之和/2. 并查集不必要动态维护集合的大小,一开始就建好并查集,提前统计好即可. 最后

【hiho】14 无间道之并查集【图论--并查集】

传送门:无间道之并查集 分析 并查集的分析可以看上面的传送门,写的挺好的了. 其实在我看来并查集就是一种方便的维护集合的一种技巧,提出了代表元素这一概念. My AC Code #include<bits/stdc++.h> #define rep(i,a,b) for(int i=a;i<=b;i++) using namespace std; const int maxn=1e5+5; int represent[maxn]; int find_represent(int x) {

CodeForces 745C Hongcow Builds A Nation 并查集

题意: 给了你n个城市 m条边 k个政府 每个政府管辖的区域内不能和其他政府的区域有相连 即政府之间不存在路径 问你在维护这种关系的同时 最多再加多少条边 思路: 先找出来每个联通块 再找出来没有归属的孤立的点 把他们都放到最大的联通块里 然后每个联通块之间的点两两连边是n*(n-1)/2条边 最后算出来的ans-m就好了 (看别人的博客学了一个max_element 1 #include<bits/stdc++.h> 2 #define cl(a,b) memset(a,b,sizeof(a

并查集(个人模版)

并查集: 1 int find(int a) 2 { 3 int r=a; 4 while(f[r]!=r) 5 r=f[r]; 6 int i=a; 7 int j; 8 while(i!=r) 9 { 10 j=f[i]; 11 f[i]=r; 12 i=j; 13 } 14 return r; 15 } 16 int merge(int a,int b) 17 { 18 int A,B; 19 A=find(a); 20 B=find(b); 21 if(A!=B) 22 { 23 f[B

并查集应用

题目描述: One way that the police finds the head of a gang is to check people's phone calls. If there is a phone call between A and B, we say that A and B is related. The weight of a relation is defined to be the total time length of all the phone calls

HDU 5441 离线处理 + 并查集

题意:给n个节点m条带权值边的无向图.然后q个问题,每次询问点对的数目,点对需要满足的条件是:1)连通:2)其路径的最大权值不能超过询问值. 分析:如果没次询问一次,dfs一次,很可能超时,因此可以用并查集.离线处理,把边按权值排序,把问题按大小排序.然后离线的过程就是不断向图中加边的过程. 比如样例如下: 然后离线处理,排完序后将会是一条一条的加边:问题也排了序,因此是个累加过程... 1 #include <cstdio> 2 #include <iostream> 3 #in

poj1988 Cube Stacking(并查集

题目地址:http://poj.org/problem?id=1988 题意:共n个数,p个操作.输入p.有两个操作M和C.M x y表示把x所在的栈放到y所在的栈上(比如M 2 6:[2 4]放到[1 6]上为[2 4 1 6]),C x为输出x下面有几个数. 思路:并查集每个集合以栈最下面的数为根,维护两个数组num[x]表示x所在集合节点总数,count[x]表示x下方节点个数.每次查找压缩路径的时候更新count(换父节点的时候每轮都把父节点的count加给儿子,就可以一直更新到x所在栈