前言
数据结构-并查集
并查集是一种特殊的数据结构,其基本功能就是查询某个编号结点的祖先结点和合并两个结点,将这两个结点加入同一个集合中,至于并查集的含义有很多博客都是有讲过并且给出了很形象的例子来说明这一数据结构,这里不做过多赘述
一、并查集(Djset)是什么?
并查集是一种树型的数据结构,用于处理一些不相交集合的合并及查询问题(即所谓的并、查)。比如说,我们可以用并查集来判断一个森林中有几棵树、某个节点是否属于某棵树等。并查集一般用来解决连通性问题,比如说在一个森林中有多少颗树,这些树的根节点是什么,可以用并查集来解决。
二、具体代码
python实现下的并查集
代码如下:
class Djset:
def __init__(self,n):
# 初始传入结点个数
self.n = n
self.cnt = n # 记录集合的个数,在进行合并操作后应当减少
self.parent = [i for i in range(self.n)] # 初始每个结点都没有并入集合,每个结点的父结点都是自己本身
self.rank = [1 for i in range(self.n)] # 秩,通过按秩排序可以优化时间复杂度
def find(self,x):
# 查询根结点的方法
if x != self.parent[x]:
self.parent[x] = self.find(self.find(self.parent[x]))
return self.parent[x]
def merge(self,x,y):
# 合并两个集合的方法
rootx = self.find(x)
rooty = self.find(y)
if rootx != rooty:
if self.rank[rootx] > self.rank[rooty]: # 说明x的秩大于y的秩,需要将x接到y的根节点上面
self.parent[rooty] = rootx
elif self.rank[rootx] < self.rank[rooty]:
self.parent[rootx] = rooty
elif self.rank[rootx] == self.rank[rooty]:
self.rank[rootx] += 1
self.cnt -= 1
上面的并查集是优化过的并查集,在合并的时候按秩合并,从而减少查询的时间
1971. 寻找图中是否存在路径 - 力扣(LeetCode)
有一个具有
n
个顶点的 双向 图,其中每个顶点标记从0
到n - 1
(包含0
和n - 1
)。图中的边用一个二维整数数组edges
表示,其中edges[i] = [ui, vi]
表示顶点ui
和顶点vi
之间的双向边。 每个顶点对由 最多一条 边连接,并且没有顶点存在与自身相连的边。请你确定是否存在从顶点
source
开始,到顶点destination
结束的 有效路径 。给你数组
edges
和整数n
、source
和destination
,如果从source
到destination
存在 有效路径 ,则返回true
,否则返回false
。
ac代码:
class Djset:
def __init__(self,size):
self.parent = list(range(size))
self.rank = [1] * size
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]:
self.parent[rootX] = rootY
elif self.rank[rootX] > self.rank[rootY]:
self.parent[rootY] = rootX
else:
self.parent[rootX] = rootY
self.rank[rootY] += 1
class Solution:
def validPath(self, n: int, edges: List[List[int]], source: int, destination: int) -> bool:
d = Djset(n)
for i in range(len(edges)):
d.union(edges[i][0],edges[i][1]) # 相连的地方加入同一个集合
return d.find(source) == d.find(destination)
c++实现下的并查集
代码如下:
#include <iostream>
#include <vector>
using namespace std;
class Djset {
public:
vector<int> parent; // 记录节点的根
vector<int> rank; // 记录根节点的深度(用于优化)
int count;
Djset(int n): parent(vector<int>(n)), rank(vector<int>(n)), count(n) {
for (int i = 0; i < n; i++) {
parent[i] = i;
}
}
int find(int x) {
// 压缩方式:直接指向根节点
if (x != parent[x]) {
parent[x] = find(parent[x]);
}
return parent[x];
}
void merge(int x, int y) {
int rootx = find(x);
int rooty = find(y);
if (rootx != rooty) {
if (rank[rootx] < rank[rooty]) {
swap(rootx, rooty);
}
parent[rooty] = rootx;
if (rank[rootx] == rank[rooty]) rank[rootx] += 1;
count--;
}
}
int get_count() {
return count;
}
};
547. 省份数量 - 力扣(LeetCode)
有
n
个城市,其中一些彼此相连,另一些没有相连。如果城市a
与城市b
直接相连,且城市b
与城市c
直接相连,那么城市a
与城市c
间接相连。省份 是一组直接或间接相连的城市,组内不含其他没有相连的城市。
给你一个
n x n
的矩阵isConnected
,其中isConnected[i][j] = 1
表示第i
个城市和第j
个城市直接相连,而isConnected[i][j] = 0
表示二者不直接相连。返回矩阵中 省份 的数量。
ac代码:
class Djset {
public:
vector<int> parent; // 记录节点的根
vector<int> rank; // 记录根节点的深度(用于优化)
int count;
Djset(int n): parent(vector<int>(n)), rank(vector<int>(n)), count(n) {
for (int i = 0; i < n; i++) {
parent[i] = i;
}
}
int find(int x) {
// 压缩方式:直接指向根节点
if (x != parent[x]) {
parent[x] = find(parent[x]);
}
return parent[x];
}
void merge(int x, int y) {
int rootx = find(x);
int rooty = find(y);
if (rootx != rooty) {
if (rank[rootx] < rank[rooty]) {
swap(rootx, rooty);
}
parent[rooty] = rootx;
if (rank[rootx] == rank[rooty]) rank[rootx] += 1;
count--;
}
}
int get_count() {
return count;
}
};
class Solution {
public:
int findCircleNum(vector<vector<int>>& a) {
Djset d(a.size());
for (int i = 0; i < a.size(); ++i) {
for (int j = i + 1; j < a.size(); ++j) {
if (a[i][j]) {d.merge(i,j);}
}
}
return d.get_count();
}
};
总结
其实这些题目总结一下就是求连通分量的问题,也可以通过dfs来解决,但是并查集作为一种数据结构还是很重要的
个人学习过程中的总结,如果有问题希望大佬指出
谢谢你看到这里