C语言建立并查集

news2024/12/27 15:47:54

一.树的存储方式

在知道并查集之前,我们得知道树的三种存储方式:

1.双亲表示法

双亲表示法 :双亲表示法是最简单的一种存储方式,它使用一个大小为n的一维数组来表示树中的n个节点。在数组中,每个元素存储该节点的父节点的下标,根节点的父节点下标为-1。由于每个节点只有一个父节点,因此可以快速找到一个节点的父节点,但查找一个节点的子节点需要遍历整个数组,效率较低。

用C语言定义其存储结构:

#define MAX_TREE_SIZE 100

typedef struct {
    int data;        // 节点数据
    int parent;      // 父节点在数组中的下标
} PTNode;

typedef struct {
    PTNode nodes[MAX_TREE_SIZE];    // 存储节点的数组
    int root;                       // 根节点在数组中的下标
    int size;                       // 树中节点的个数
} PTree;

在上面的代码中,PTNode结构体表示树中的一个节点,包含节点的数据和其父节点在数组中的下标。PTree结构体表示整棵树,包括存储节点的数组、根节点在数组中的下标和树中节点的个数。可以根据需要调整MAX_TREE_SIZE的值来适应具体情况。

本次实验并查集就是用这种方法表示的

2.孩子表示法

孩子表示法采用链式存储结构,每个节点包含指向其第一个子节点的指针和指向其下一个兄弟节点的指针。如果一个节点没有子节点,则其子节点指针为空。孩子表示法的优点是查找一个节点的子节点很方便,但查找一个节点的父节点需要遍历整棵树。

这种方法我们最熟悉了,前面的二叉树和哈夫曼树的相关操作,我们都是用这种方法。

用C语言实现其存储结构:

#define MAX_TREE_SIZE 100

typedef struct CTNode {
    int child;                  // 子节点在数组中的下标
    struct CTNode* next;        // 指向下一个兄弟节点的指针
} *ChildPtr;

3.孩子兄弟表示法(二叉树表示法)

孩子兄弟表示法也采用链式存储结构,每个节点包含指向其第一个子节点的指针和指向其下一个兄弟节点的指针。如果一个节点没有子节点,则其子节点指针为空;如果一个节点没有兄弟节点,则其兄弟节点指针为空。孩子兄弟表示法可以方便地遍历树的所有节点,但查找一个节点的父节点需要遍历整棵树。

下面是使用C语言定义孩子兄弟表示法存储结构的示例代码:

#define MAX_TREE_SIZE 100

typedef struct CSNode {
    int data;                   // 节点数据
    struct CSNode* firstchild;  // 指向第一个子节点的指针
    struct CSNode* nextsibling; // 指向下一个兄弟节点的指针
} CSNode, *CSTree;

上述代码中,CSNode结构体表示树中的每个节点,包括节点的数据、指向第一个子节点的指针和指向下一个兄弟节点的指针。CSTree类型表示整棵树,实际上就是一个指向根节点的指针。可以根据需要调整MAX_TREE_SIZE的值来适应具体情况。与前两种存储方式不同,孩子兄弟表示法不需要额外的数组或链表来存储节点,而是直接使用节点之间的指针关系来表示树的结构。

二.并查集

1.基础认识

并查集是一种树型的数据结构,用于处理一些不相交集合(Disjoint Sets)的合并及查询问题。它支持两种操作:查找和合并。

在并查集中,每个元素有一个父节点,如果一个元素的父节点是自己,说明该元素是一个集合的代表元素,称之为该集合的“祖先”。通过查找该元素的祖先,可以得到该元素所属的集合。同时,如果两个元素的祖先不同,则可以将它们所属的两个集合进行合并。

并查集常见的应用场景包括图像处理、网络连通性问题、路径压缩等领域。

并查集的实现方式有多种,其中最常见的是基于数组实现的“按秩合并”和“路径压缩”算法。按秩合并指在合并两个集合时,将元素较少的集合合并到元素较多的集合中,并更新新集合的秩;路径压缩指在查找元素的祖先时,将路径上所有经过的节点都直接连接到祖先节点,以减小后续查找的复杂度。

2.说明

这是408考研从2022年后新增的考点,不难,但记得掌握。

这是王道考研考点要求图:

9rtI.jpg

三.核心部分实现

1.定义结构体:

定义并查集结构体 首先需要定义一个并查集的结构体,包含元素个数、秩数组和父节点数组:

#include <stdio.h>
#include <stdlib.h>
#define size 10
int parents[size];       //集合元素数组(双亲指针数组)
int high[size];          //记录每个集合的树高

/*这里解释一下,因为采用的是双亲表示法,所以我们用的是数组存储,数组的下标就是结点的编号
 这里为了举例,我们假设结点最大为10*/

2.初始化

初始化并查集 初始化并查集时,将每个元素的父节点指向自身:

//并查集的初始化(S即是并查集)
void Initial(int S[]){
	for(int i=0;i<size;i++){        //这里初始每个元素自己就是一个集合
		S[i]=-1;
		high[i]=1;                 //初始树高设为1
	}
}

3.“查”函数

查找元素的祖先节点 查找元素的祖先节点时,需要沿着父节点不断向上查找,直到找到根节点为止。

//定义“查”函数
int find1(int S[],int x){             //因为后面要写优化的find算法,这里叫find1
	while(S[x]>0)                    //循环寻找x所在集合的根
		x=S[x];
	return x;                        //根的S[]小于0
}

4.“并”函数

合并两个集合 合并两个集合时,可以根据秩数组的大小来判断哪个集合的根节点应该成为新集合的根节点。同时,在将新集合的根节点挂在旧集合的根节点下面时,需要更新秩数组:

//定义“并”函数
void union1(int S[],int root1,int root2){
	if(root1==root2)               //要求root1和root2是不同的集合
		return;
	S[root2]=root1;                //将根root2连接到另一根root1下面
}
/*这里root1和root2的编号,你可以举个例子,比如这里root1=2,root2=3,分别代表下标为2和3的集合,
  把root2的双亲定为root1,就相当于把root2连接到另一根root1下面*/

四.优化操作

1.优化原理

并查集的优化主要是通过路径压缩和按秩合并两种方法来实现的。

路径压缩是指在查找根节点的同时,将搜索路径上遇到的所有节点都直接连接到根节点,以减少下一次查找所需的时间。它可以通过递归或迭代的方式来实现。具体来说,在查找根节点时,可以沿着路径向上递归或循环查找,并将搜索路径上的所有节点直接连接到根节点。这样做可以使得后续的查找操作变得更加高效,因为每个节点都会直接连接到根节点,而不需要再重新访问路径上的其他节点。

按秩合并是指将两个集合按照它们的秩(即节点数)进行合并,从而保证较小的集合被连接在较大的集合上,进一步减少了路径压缩的深度。具体来说,当需要将两个集合合并时,先比较它们的秩大小,然后将秩较小的集合连接到秩较大的集合上。这样做可以保证较小的集合被合并到较大的集合上,从而减少了路径压缩的深度,提高了并查集的查询性能。

上面两种优化方法,分别对应着优化“查”和优化“并”操作

2.优化代码

//定义“查”的优化算法
int find(int x) {
	if (x != parents[x]) {
		parents[x] = find(parents[x]);                    //把x元素的双亲直接修改为根结点
	}
	return parents[x];
}
/*如果不理解可以举个例子,比如2合并到7下面,然后把3合并到2下面,这样就相当于把3合并到7的集合里,但是查早3的根结点是先找到2再找到7
  如果我们在找到3的时候把3从2下面直接合并到根结点7下面,这样以后查找的时候,我们可以节省时间复杂度*/

//定义“并”的优化算法
bool union2(int x,int y){
	x = find1(parents,x);						//寻找 x的集合根
	y = find1(parents,y);						//寻找 y的集合根
	if(x == y)                                  //如果两个在同一个集合里,我们就不合并
		return false;
	else								//否则
	{
		if(high[x]==high[y]) high[y]++;	//如果 x的高度和 y的高度相同,则令 y的高度加1
		parents[x]=y;						//让 x的双亲,也就是把x合并到y集合里
	}
	return true;
}

五.C语言完整测试代码

#include <stdio.h>
#include <stdlib.h>
#define size 10
int parents[size];       //集合元素数组(双亲指针数组)
int high[size];          //记录每个集合的树高

/*这里解释一下,因为采用的是双亲表示法,所以我们用的是数组存储,数组的下标就是结点的编号
 这里为了举例,我们假设结点最大为10*/

//并查集的初始化(S即是并查集)
void Initial(int S[]){
	for(int i=0;i<size;i++){        //这里初始每个元素自己就是一个集合
		S[i]=-1;
		high[i]=1;                 //初始树高设为1
	}
}

//定义“查”函数
int find1(int S[],int x){             //因为后面要写优化的find算法,这里叫find1
	while(S[x]>0)                    //循环寻找x所在集合的根
		x=S[x];
	return x;                        //根的S[]小于0
}

//定义“并”函数
void union1(int S[],int root1,int root2){
	if(root1==root2)               //要求root1和root2是不同的集合
		return;
	S[root2]=root1;                //将根root2连接到另一根root1下面
}
/*这里root1和root2的编号,你可以举个例子,比如这里root1=2,root2=3,分别代表下标为2和3的集合,
  把root2的双亲定为root1,就相当于把root2连接到另一根root1下面*/

//定义“查”的优化算法
int find(int x) {
	if (x != parents[x]) {
		parents[x] = find(parents[x]);                    //把x元素的双亲直接修改为根结点
	}
	return parents[x];
}
/*如果不理解可以举个例子,比如2合并到7下面,然后把3合并到2下面,这样就相当于把3合并到7的集合里,但是查早3的根结点是先找到2再找到7
  如果我们在找到3的时候把3从2下面直接合并到根结点7下面,这样以后查找的时候,我们可以节省时间复杂度*/

//定义“并”的优化算法
bool union2(int x,int y){
	x = find1(parents,x);						//寻找 x的集合根
	y = find1(parents,y);						//寻找 y的集合根
	if(x == y)                                  //如果两个在同一个集合里,我们就不合并
		return false;
	else								//否则
	{
		if(high[x]==high[y]) high[y]++;	//如果 x的高度和 y的高度相同,则令 y的高度加1
		parents[x]=y;						//让 x的双亲,也就是把x合并到y集合里
	}
	return true;
}

int main(){
	Initial(parents);                //初始化集合
	int x1=find1(parents,2);         //这里检验一下find1操作,初始化后找下标为2的集合,返回的是自己
	printf("合并操作前,含下标为2的集合此时根为:%d\n",x1);
	union1(parents,7,2);             //这里是把下标为2的集合合并到根为7的集合中
	int x2=find1(parents,2);         //这里再查找2那么输出应该和上面不同
	printf("合并操作后,含下标为2的集合此时根为:%d\n",x2);
	printf("-------------------------------------------\n");
	//到这里,我们的并查集基础操作已经完成了
	Initial(parents);              //为了测试优化的并操作,这里重新初始化一下
	int x3=find1(parents,2);         //这里检验一下find1操作,初始化后找下标为2的集合,返回的是自己
	printf("优化合并操作前,含下标为2的集合此时根为:%d\n",x3);
	union2(2,7);             //这里是把下标为2的集合合并到根为7的集合中
	int x4=find1(parents,2);         //这里再查找2那么输出应该和上面不同
	printf("优化合并操作后,含下标为2的集合此时根为:%d\n",x4);
	printf("-------------------------------------------\n");
}


六.运行结果

9MRB.jpg

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/672964.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux内核态内存泄露检测工具——Kmemleak

我的圈子&#xff1a; 高级工程师聚集地 我是董哥&#xff0c;高级嵌入式软件开发工程师&#xff0c;从事嵌入式Linux驱动开发和系统开发&#xff0c;曾就职于世界500强企业&#xff01; 创作理念&#xff1a;专注分享高质量嵌入式文章&#xff0c;让大家读有所得&#xff01; …

Windows开始菜单栏处无法直接搜索软件

文章目录 1. 打开cmd&#xff0c;输入start powershell打开PowerShell&#xff0c;然后在PowerShell中输入下面的命令&#xff0c;之后重启电脑2. 修改注册表3. 开启Windows Search服务4. 可能是搜索进程被禁用了5. 开启Cortana5.1. 打开gpedit.msc 6. 使用疑难解答6.1. 选择其…

chatgpt赋能python:Python如何查找特定名称文件

Python如何查找特定名称文件 在计算机文件管理和互联网网络应用程序中&#xff0c;查找特定文件往往是一项必要的任务。在使用Python编程时&#xff0c;我们可以使用Python内置的os模块来查找特定名称的文件。本文将介绍如何使用Python查找特定名称的文件&#xff0c;并提供实…

一个例子带你了解MapReduce

写在前面&#xff1a;博主是一只经过实战开发历练后投身培训事业的“小山猪”&#xff0c;昵称取自动画片《狮子王》中的“彭彭”&#xff0c;总是以乐观、积极的心态对待周边的事物。本人的技术路线从Java全栈工程师一路奔向大数据开发、数据挖掘领域&#xff0c;如今终有小成…

LTV-6314-ASEMI代理台湾光宝高速光耦LTV-6314

编辑&#xff1a;ll LTV-6314-ASEMI代理台湾光宝高速光耦LTV-6314 型号&#xff1a;LTV-6314 品牌&#xff1a;台湾光宝 封装&#xff1a;LSOP-6 引脚数量&#xff1a;6 类型&#xff1a;光耦 特性&#xff1a;台湾光宝、IGBT驱动器、储能专用光耦&#xff3c;高速光耦 …

如何看一块intel主板的配置的好坏

为什么写这篇文章 最近在挑13900k的主板&#xff0c;挑的眼花缭乱&#xff0c;发现主板市场不像CPU市场有那么清晰的产品线。 单拿微星一家的Z790来说&#xff0c;就包括但不限于以下型号 Z790 GAMINGZ790-PZ790-AZ790暗黑Z790刀锋 每一种的价格都不一样&#xff0c;更别弹还…

Golang每日一练(leetDay0104) 买卖股票最佳时机之含冷冻期、手续费

目录 309. 最佳买卖股票时机含冷冻期 Best-time-to-buy-and-sell-stock-with-cooldown &#x1f31f;&#x1f31f; 714. 买卖股票的最佳时机含手续费 Best-time-to-buy-and-sell-stock-with-transaction-fee &#x1f31f;&#x1f31f; &#x1f31f; 每日一练刷题专栏 …

读发布!设计与部署稳定的分布式系统(第2版)笔记08_自黑与放大

1. 自黑式攻击 1.1. 自黑只会偶尔成为人类的美德 1.2. 对系统来说&#xff0c;绝对不会推崇自黑 1.3. “自黑式攻击”是指系统或有人类参与的扩展系统联合外部对自身发起攻击 1.4. 好的营销可以随时杀死你 1.4.1. 并不是每个自黑的“伤口”&#xff0c;都可以归咎于营销部…

<C++> C++11新的类功能

C11新的类功能 1.默认成员函数 原来C类中&#xff0c;有6个默认成员函数&#xff1a; 构造函数析构函数拷贝构造函数拷贝赋值重载取地址重载const取地址重载 最后重要的是前4个&#xff0c;后两个用处不大。默认成员函数就是我们不写编译器会生成一个默认的。 C11 新增了两个…

Uniapp 开发 ①(快速上手)

作者 : SYFStrive 博客首页 : HomePage &#x1f4dc;&#xff1a; 微信小程序 &#x1f4cc;&#xff1a;个人社区&#xff08;欢迎大佬们加入&#xff09; &#x1f449;&#xff1a;社区链接&#x1f517; &#x1f4cc;&#xff1a;觉得文章不错可以点点关注 &#x1f4…

Vue3在工作中使用的一些经验总结

1、隐藏el-tab-pane 设置隐藏 2、Vue中ref的使用 3、Vue中的api 4、component: () &#xff1e; import(‘/views/order/orderDetail‘), 5、ids selections.map((i) > i.ruleId); 6、路由配置的三种方式 项目中使用到的 7、Vue3新特性 8、template在Vue中的作用 9、…

电脑装机后使用Administrator作为电脑账号

目录标题 1 搜索cmd使用管理员权限运行&#xff08;因为直接winr无法用权限打开&#xff09;2 输入net user administrator /active:yes&#xff0c;之后系统会提示命令完成&#xff0c;通过这个指令就是让系统默认账户administrator成为超级管理员&#xff0c;方便接下来的操作…

学习mysql

Mysql SQL语言的规则与规范SQL大小写规范注释数据导入指令 基本的SELECT语句SELECT.列的别名去掉重复行空值参与运算着重号(当有表名是关键字时)显示表结构where 运算符算术运算符 比较运算符号性运算符非符号形运算符空运算符非空运算符最小值运算符最大值运算符BETWEEN AND运…

python处理Excel Pandas xlwings numpy, jupyter,docx,jieba 词频统计 flash

# 批量创建Excel import xlwings # xw.App(visibleTrue,add_bookTrue) 会打开Excel&#xff0c;且不会自动关闭 # xw.App(visibleTrue,add_bookTrue) 会打开Excel&#xff0c;但一晃就自动关闭了 app xlwings.App(visibleTrue, add_bookFalse) for language in [Java, Pyt…

C++——set/multiset再理解

目录 1. 关联式容器 2. 键值对 3. 树形结构的关联式容器 4. set容器 4.1 set的介绍 4.2 set的使用 5. multiset 5.1 multiset的介绍 5.2 mutiset的使用 1. 关联式容器 先前我们已经接触过STL中的部分容器&#xff0c;比如&#xff1a;vector、list、deque、forward_…

边缘计算技术主要有哪几种?如何应用在实际场景中?

边缘计算是一种新的计算架构&#xff0c;它将计算资源移动到靠近终端用户的边缘设备中&#xff0c;以实现更快、更可靠、更安全的数据传输和处理。边缘AI智能则是指将人工智能算法和模型部署到边缘设备上&#xff0c;使其能够在设备本身上执行计算和决策&#xff0c;而不需要发…

【unity每日一记】资源和场景加载图文详解

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 秩沅 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a;uni…

Jmeter(一) - 从入门到精通 - 环境搭建(详解教程)

1.JMeter 介绍 Apache JMeter是100%纯JAVA桌面应用程序&#xff0c;被设计为用于测试客户端/服务端结构的软件(例如web应用程序)。它可以用来测试静态和动态资源的性能&#xff0c;例如&#xff1a;静态文件&#xff0c;Java Servlet,CGI Scripts,Java Object,数据库和FTP服务器…

Netty的零拷贝

技术主题 netty本质上就是一款优秀的网络编程框架&#xff0c;凭借自己基于NIO编程&#xff0c;零拷贝等技术细节&#xff0c; 技术原理 零拷贝机制&#xff08;zero-copy&#xff09;是在操作数据时不需要将数据从一块内存复制到另一块内存区域的技术&#xff0c;这样就避免…

Docker学习笔记1

PaaS&#xff1a; 一、虚拟化分类&#xff1a; 虚拟化资源提供者&#xff1a; 1&#xff09;硬件平台虚拟化 2&#xff09;操作系统虚拟化 虚拟化实现方式&#xff1a; type I: 半虚拟化 type II&#xff1a;硬件辅助全虚拟化 type III&#xff1a; 软件全虚拟化&#xff1a; …