个人笔记:算法讲座5——并查集

本文仅供参考学习使用,谢谢

并查集用于:
查询无向图的连通分量个数
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;
}

例题:

积木1
积木2
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值