【算法】并查集(Disjoint Set)[共3讲]_哔哩哔哩_bilibili
leetcode-广度优先遍历/图/并查集_MaYingColdPlay的博客-CSDN博客
python实现一个简单的并查集 - SegmentFault 思否
并查集板子看 找出知道秘密的所有专家
python实现各种常用算法之数据结构(7) - Hadoop_Spark的个人空间 - OSCHINA - 中文开源技术交流社区
力扣
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)
力扣
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
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)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
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 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)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
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
思路:
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)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
力扣
并查集硬模拟
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)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
力扣
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_]
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
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