并查集理论基础
应用场景:
并查集常用来解决连通性问题。
大白话就是当我们需要判断两个元素是否在同一个集合里的时候,我们就要想到用并查集。
功能:
- 初始化
- 寻找根节点,函数:find(int u),也就是判断这个节点的祖先节点是哪个
- 将两个节点接入到同一个集合,函数:join(int u, int v),将两个节点连在同一个根节点上
- 判断两个节点是否在同一个集合,函数:isSame(int u, int v),就是判断两个节点是不是同一个根节点
实现的数据结构:
只需要用一个一维数组来表示,即:father[A] = B,father[B] = C 这样就表述 A 与 B 与 C连通了(有向连通图)。相当于记录根结点!
经过路径压缩,直接指向最初的根结点
代码:
初始化:需要让每个节点都指向自己
cpp
// 并查集初始化
void init() {
for (int i = 0; i < n; ++i) {
father[i] = i;
}
}
查找根
可进行路径压缩优化
cpp
// 并查集里寻根的过程
int find(int u) {
if (u == father[u]) return u; // 如果根就是自己,直接返回
else return find(father[u]); // 如果根不是自己,就根据数组下标一层一层向下找
}
判断两个元素是否在同一个集合里
cpp
bool isSame(int u, int v) {
u = find(u);
v = find(v);
return u == v;
}
**路径压缩:一旦应用find,直接把孩子连在根结点上
cpp
// 并查集里寻根的过程
int find(int u) {
if (u == father[u]) return u;
else return father[u] = find(father[u]); // 路径压缩
}
进一步优化:合树的时候,矮树并入高树
用路径压缩记录rank就不准了!所以不用
cpp
void join(int u, int v) {
u = find(u); // 寻找u的根
v = find(v); // 寻找v的根
if (rank[u] <= rank[v]) father[u] = v; // rank小的树合入到rank大的树
else father[v] = u;
if (rank[u] == rank[v] && u != v) rank[v]++; // 如果两棵树高度相同,则v的高度+1,因为上面 if (rank[u] <= rank[v]) father[u] = v; 注意是 <=
}
107. 寻找存在的路径
卡码网题目链接(ACM模式)(opens new window)
题目描述
给定一个包含 n 个节点的无向图中,节点编号从 1 到 n (含 1 和 n )。
你的任务是判断是否有一条从节点 source 出发到节点 destination 的路径存在。
输入描述
第一行包含两个正整数 N 和 M,N 代表节点的个数,M 代表边的个数。
后续 M 行,每行两个正整数 s 和 t,代表从节点 s 与节点 t 之间有一条边。
最后一行包含两个正整数,代表起始节点 source 和目标节点 destination。
输出描述
输出一个整数,代表是否存在从节点 source 到节点 destination 的路径。如果存在,输出 1;否则,输出 0。
cpp
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <math.h>
int find(int *father,int q){
if(q==father[q]) return q;//自己 和 根结点相同------自己就是根结点,直接return自己
else {
father[q]=find(father,father[q]);//不相同,则查找自己的上一层结点,一直到q==father【q】的情况才返回
return father[q];
}
}
void join(int x,int y,int* father){
int hx=find(father,x);
int hy=find(father,y);
if(hx==hy) return;
father[hx]=hy;
}
bool issame(int x,int y,int*father){
int hx=find(father,x);
int hy=find(father,y);
if(hx==hy) return true;
else return false;
}
int main(){
int m,n;
scanf("%d%d",&n,&m);//n是结点个数,m是边的个数
int *father=(int *)malloc(sizeof(int)*(n+1));
for (int i=0;i<n+1;i++) father[i]=i;
for(int i=0;i<m;i++){
int x,y;
scanf("%d%d",&x,&y);
join(x,y,father);
}
int source,destination;
scanf("%d%d",&source,&destination);
printf("%d",issame(source,destination,father));
return 0;
}