本文仅供参考学习使用,谢谢
并查集用于:
查询无向图的连通分量个数
or
无向图中任何两个顶点是否联通
并查集的基本应用:
两个方面
1.查找search(find)
找到某一个位置的最高上级,即跟结点下标
PS:优化处理时 将需要找最高上级的节点 直接上级直接赋值为 最高上级,以减少查询次数降低时间复杂度
//查找
int find(int x){//x为需要找最高上级(跟结点下标) 的节点
//这三部即可找到该节点的最高上级下标 但是如果每一次都这么找的话会花费很多时间去做双亲到孩子的查询
int r=x;
while (set[r]!=r)//set[r]上的记号实际上是跟结点区别于其他节点的记号,只要不满足根节点的特殊标记,就可以不断while循环下去
r=set[r];
//结束上部分的查询x节点的根结点下标之后 r 代表着x节点的跟结点下标
//优化处理部分:将每一次找到最高上级之后的节点x 的直接上级设置为该根结点 这样第二次去查找的时候时间复杂度为O(n)
int i=x,j;//i记录 从x节点到根节点r的过程中 经过的节点的下标, j用于记录每一次找到的直接上级的数组下标
while (i!=r) {//只要没有到达根节点就不断进行while循环
j=set[i];//j记录当前节点的直接上级下标
set[i]=r;//把这个当前结点的直接上节改为根节点下标 用以优化
i=j;//将当前结点的直接上级给i, 继续优化直接上级 直到优化到根节点为止
}
return r;//返还x节点的直接最高上级(根节点下标)
}
2.合并merge(join)
如果想要在两个不同最高上级的节点之间建立联系
需要将两个节点的最高上级进行合并处理,即将其中一个最高上级化为另一个最高上级的直接下级
(其实这个合并也可以进行优化,将较矮的树插到较高的树的下面即可)
//记录某个节点的高度
int count(int x){
int count = 0 , r = x;
while(set[r]!=r){
r=set[r];
count++;
}
return count;
}
//合并
void jion(int a, int b){//在ab之间建立一条联系
int find_a=find(a);//找到a的根节点下标
int find_b=find(b);//找到b的根节点下标
if (find_a!=find_b) //如果ab两个节点的根节点不是同一个节点的话 就在两个节点之间建立联系
if(count(find_a)>count(find_b)){
set[find_b]=find_a;//将b节点的根节点 化为a节点的根节点的直接下级
}else {
set[find_a]=find_b;//将a节点的根节点 化为b节点的根节点的直接下级
}
}
代码
//
// main.c
// 并查集
//
// Created by xxc on 2020/3/28.
// Copyright © 2020 xxc. All rights reserved.
//
/*
并查集的基本应用:
两个方面
1.查找search(find)
找到某一个位置的最高上级,即跟结点下标
PS:优化处理时 将需要找最高上级的节点 直接上级直接赋值为 最高上级,以减少查询次数降低时间复杂度
2.合并merge(join)
如果想要在两个不同最高上级的节点之间建立联系
需要将两个节点的最高上级进行合并处理,即将其中一个最高上级化为另一个最高上级的直接下级
*/
#include <stdio.h>
#define maxsize 1000
int set[maxsize];
int flag[maxsize]={0};
//查找
int find(int x){//x为需要找最高上级(跟结点下标) 的节点
//这三部即可找到该节点的最高上级下标 但是如果每一次都这么找的话会花费很多时间去做双亲到孩子的查询
int r=x;
while (set[r]!=r)//set[r]上的记号实际上是跟结点区别于其他节点的记号,只要不满足根节点的特殊标记,就可以不断while循环下去
r=set[r];
//结束上部分的查询x节点的根结点下标之后 r 代表着x节点的跟结点下标
//优化处理部分:将每一次找到最高上级之后的节点x 的直接上级设置为该根结点 这样第二次去查找的时候时间复杂度为O(n)
int i=x,j;//i记录 从x节点到根节点r的过程中 经过的节点的下标, j用于记录每一次找到的直接上级的数组下标
while (i!=r) {//只要没有到达根节点就不断进行while循环
j=set[i];//j记录当前节点的直接上级下标
set[i]=r;//把这个当前结点的直接上节改为根节点下标 用以优化
i=j;//将当前结点的直接上级给i, 继续优化直接上级 直到优化到根节点为止
}
return r;//返还x节点的直接最高上级(根节点下标)
}
//记录某个节点的高度
int count(int x){
int count = 0 , r = x;
while(set[r]!=r){
r=set[r];
count++;
}
return count;
}
//合并
void jion(int a, int b){//在ab之间建立一条联系
int find_a=find(a);//找到a的根节点下标
int find_b=find(b);//找到b的根节点下标
if (find_a!=find_b){ //如果ab两个节点的根节点不是同一个节点的话 就在两个节点之间建立联系
if(count(find_a)>=count(find_b)){//b的高度较小
set[find_b]=find_a;//将b节点的根节点 化为a节点的根节点的直接下级
} else {//a的高度较小
set[find_a]=find_b;//将a节点的根节点 化为b节点的根节点的直接下级
}
}
}
合并(未优化)
//void jion(int a, int b){//在ab之间建立一条联系
// int find_a=find(a);//找到a的根节点下标
// int find_b=find(b);//找到b的根节点下标
//
// if (find_a!=find_b) //如果ab两个节点的根节点不是同一个节点的话 就在两个节点之间建立联系
// set[find_b]=find_a;//将b节点的根节点 化为a节点的根节点的直接下级
//}
//初始化
void INIT(int N,int M){//初始时一共有N个节点,M个关系
for (int i=0; i<=N; i++) {//初始化每一个节点
set[i]=i;
}
int a,b;//建立M条联系
for (int i=1; i<=M; i++) {
scanf("%d%d",&a,&b);
jion(a, b);//在ab之间建立联系
}
for (int i=1; i<=N; i++) //标记所有的根节点
flag[find(i)]=1;//标记第i个节点的根节点为根节点
}
void checkout(int N){
for (int i=1; i<=N; i++) {
printf("%3d",set[i]);
}
printf("\n");
}
int main(int argc, const char * argv[]) {
int N,M;
scanf("%d%d",&N,&M);
INIT(N, M);
checkout(N);
printf("%d",find(4));
jion(5, 7);
printf("%d",find(7));
return 0;
}
例题: