数据结构与算法(陈越版)第五讲 (树下)树的应用——集合及其运算
数据结构与算法(陈越版)第五讲 (树下)树的应用——集合及其运算
- 一、集合的表示
- 1.1、集合的表示
- 1.2、集合的储存
- 二、集合的运算
- 2.1查找以及普通并
- 2.2按照秩的合并算法
- 2.3路径压缩优化
一、集合的表示
集合是一种常见的数据表示方式。集合的运算包括交,并,补,差以及判定一个数据是否是某个集合的元素。
为了解决上述问题就需要用到集合,准确的说是集合的运算,因此就需要合理的进行集合的表示,既然要对集合表示那就需要考虑集合存储实现。
1.1、集合的表示
为了有效地对集合执行各种操作,可以用树结构表示集合:用树根代表这个集合,数的每个结点代表一个集合元素。
采用树结构的好处:判定一个数据是否属于一个集合是很方便的(参考树中查找元素)
如上图所示:与之前的父子关系指针不同,这里结点的指针不是从父亲指向儿子,而是儿子指向父亲。
1.2、集合的储存
既然是树结构那么就可以用数组进行储存。
typedef struct
{ElementType Data;int parent;
}SetType;
二、集合的运算
这里主要是处理并查集,所以只考虑如何查以及如何并。
2.1查找以及普通并
# include <iostream>
# include <malloc.h>
# define MaxSize 100 // 切记宏定义没有分号 # define MaxSize 100; 这样在下面使用MaxSize,是不可用的。
using namespace std;typedef int ElementType;typedef struct
{ElementType Data; // 存值int parent; // 指向父结点 也就是下标
}SetType;// 查找操作 查找某个元素是否是某个集合的元素,如果是返回其根结点在数组中的下标
int Find(SetType s[], ElementType X)
{int i; for (i = 0; i < MaxSize && s[i].Data != X; i++);if (MaxSize <= i) // 没找打返回-1return -1;// 找到了for (; s[i].parent >= 0; i = s[i].parent);return i;
}// 并操作
void Union(SetType s[], ElementType x1, ElementType x2)
{int root1 = Find(s, x1); // 查询x1属于的树int root2 = Find(s, x2); // 查询x2属于的树if (root2 != root1)s[root1].parent = root2;
}int main()
{// 创建储存数组的集合SetType s[MaxSize];// 初始话数组,父结点都指向-1for (int i = 0; i < MaxSize; ++i){s[i].Data = i;s[i].parent = -1;}cout << Find(s, 5) << endl; // 随机查看一个元素属于那棵树// 并操作Union(s, 1, 2);Union(s, 2, 3);Union(s, 3, 4);cout << Find(s, 3) << endl;cout << Find(s, 2) << endl;cout << Find(s, 4) << endl;return 0;
}
但是上述并操作存在一个很严重的问题,就是会导致树越来越长,树的高度为N的树。
假设有NNN个各自独立的元素:
- 合并1和0所在的集合:
Union(s, 1, 0)
的结果是生成根为1,高度为2的树; - 合并2和0所在的集合:
Union(s, 2, 1)
的结果是生成根为2,高度为3的树; - …
- 合并NNN和0所在的集合:
Union(s, N, N-1)
的结果是生成根为NNN,高度为N+1N+1N+1的树;
此图是4个结点,进行并操作的所构成的树,所以在执行查找的过程计算复杂度为O(N2)O(N^2)O(N2)。
查找算法已经确定,而并算法可以优化。
2.2按照秩的合并算法
算法思想是:将小规模的树挂在大规模的树上。树根父结点下标为所含结点个数的负值。
# include <iostream>
# include <malloc.h>
# define MaxSize 100 // 切记宏定义没有分号 # define MaxSize 100; 这样在下面使用MaxSize,是不可用的。
using namespace std;typedef int ElementType;typedef struct
{ElementType Data; // 存值int parent; // 指向父结点 也就是下标
}SetType;// 查找操作 查找某个元素是否是某个集合的元素,如果是返回其根结点在数组中的下标
int Find(SetType s[], ElementType X)
{int i; for (i = 0; i < MaxSize && s[i].Data != X; i++);if (MaxSize <= i) // 没找打返回-1return -1;// 找到了for (; s[i].parent >= 0; i = s[i].parent);return i;
}// 并操作
void Union(SetType s[], ElementType x1, ElementType x2)
{int root1 = Find(s, x1); // 查询x1属于的树int root2 = Find(s, x2); // 查询x2属于的树if (root2 != root1)s[root1].parent = root2;
}// 按秩合并
void Union1(SetType s[], ElementType x1, ElementType x2)
{int root1 = Find(s, x1);int root2 = Find(s, x2);if (s[root1].parent <= s[root2].parent) // 通过比较根结点的父下标{ // 负数比较,小的所含结点多,树规模大 root2挂在root1树下s[root1].parent += s[root2].parent; // 合并后更新结点数量s[root2].parent = root1;}else{s[root2].parent += s[root1].parent; s[root1].parent = root2;}
}int main()
{// 创建储存数组的集合SetType s[MaxSize];// 初始话数组,父结点都指向-1for (int i = 0; i < MaxSize; ++i){s[i].Data = i;s[i].parent = -1;}cout << Find(s, 5) << endl; // 随机查看一个元素属于那棵树// 按秩并操作// 树1Union1(s, 0, 1);Union1(s, 1, 2);Union1(s, 2, 3);Union1(s, 3, 4);cout << Find(s, 3) << endl;cout << Find(s, 2) << endl;cout << Find(s, 4) << endl;cout << Find(s, 1) << endl; // 返回都是根结点0cout << "树1的结点数: " << s[0].parent << endl; // 树1 五个结点 所以输出为-5// 树2Union1(s, 50, 49);Union1(s, 51, 50);cout << Find(s, 49) << endl;cout << Find(s, 51) << endl; // 返回都是根结点50cout << "树2的结点数: " << s[50].parent << endl; // 树2 三个结点 所以输出为-3// 合并两棵树Union1(s, 4, 49);cout << Find(s, 4) << endl;cout << Find(s, 49) << endl; // 返回都是根结点0cout << "合并树的结点数: " << s[0].parent << endl; // 共有8个结点 输出为-8return 0;
}
上图是按秩合并算法的图解。
2.3路径压缩优化
查找不可避免的越查越深,路径压缩可以把待查找结点与根结点之间的一系列结点的上一结点都变为根结点,即当查找 D 后:
// 查找
# include <iostream>
# include <malloc.h>
# define MaxSize 100 // 切记宏定义没有分号 # define MaxSize 100; 这样在下面使用MaxSize,是不可用的。
using namespace std;typedef int ElementType;typedef struct
{ElementType Data; // 存值int parent; // 指向父结点 也就是下标
}SetType;// 查找操作 查找某个元素是否是某个集合的元素,如果是返回其根结点在数组中的下标
int Find(SetType s[], ElementType X)
{int i; for (i = 0; i < MaxSize && s[i].Data != X; i++);if (MaxSize <= i) // 没找打返回-1return -1;// 找到了for (; s[i].parent >= 0; i = s[i].parent);return i;
}// 并操作
void Union(SetType s[], ElementType x1, ElementType x2)
{int root1 = Find(s, x1); // 查询x1属于的树int root2 = Find(s, x2); // 查询x2属于的树if (root2 != root1)s[root1].parent = root2;
}// 按秩合并
void Union1(SetType s[], ElementType x1, ElementType x2)
{int root1 = Find(s, x1);int root2 = Find(s, x2);if (s[root1].parent <= s[root2].parent) // 通过比较根结点的父下标{ // 负数比较,小的所含结点多,树规模大 root2挂在root1树下s[root1].parent += s[root2].parent; // 合并后更新结点数量s[root2].parent = root1;}else{s[root2].parent += s[root1].parent; s[root1].parent = root2;}
}// 路径压缩查找方式
int Find1(SetType s[], ElementType x)
{int i = 0;for (; i < MaxSize && s[i].Data != x; ++i);if (s[i].parent < 0)return x;elsereturn s[i].parent = Find1(s, s[i].parent);}
int main()
{// 创建储存数组的集合SetType s[MaxSize];// 初始话数组,父结点都指向-1for (int i = 0; i < MaxSize; ++i){s[i].Data = i;s[i].parent = -1;}cout << Find(s, 5) << endl; // 随机查看一个元素属于那棵树// 并操作Union(s, 0, 1);Union(s, 1, 2);Union(s, 2, 3);Union(s, 3, 4);cout << s[0].parent << endl; // 0父结点在数组中下标为:1cout << s[1].parent << endl; // 1父结点在数组中下标为:2cout << s[2].parent << endl; // 2父结点在数组中下标为:3cout << s[3].parent << endl; // 3父结点在数组中下标为:4int root = Find1(s, 0);cout << s[0].parent << endl; // 0父结点在数组中下标为:4cout << s[1].parent << endl; // 1父结点在数组中下标为:4cout << s[2].parent << endl; // 2父结点在数组中下标为:4cout << s[3].parent << endl; // 3父结点在数组中下标为:4return 0;
}
路径压缩优化查找图解。(需要注意的是,这里我是故意设置了,元素与其在数组中元素的下标相同,无视了元素查找其在数组中的下标,如果这两者不相同,则需要先设置一个查找函数,先把元素所在下标找到)
// 返回元素在数组中的下标
int Find_X_(SetType s[], ElementType x)
{ int i = 0;for(; i < MaxSize && s[i].Data != X; ++i);return i;
}// 输出改成
cout << s[Find_X_(s,0)].parent << endl;
数据结构与算法(陈越版)第五讲 (树下)树的应用——集合及其运算相关推荐
- 常见数据结构和算法实现(排序/查找/数组/链表/栈/队列/树/递归/海量数据处理/图/位图/Java版数据结构)
常见数据结构和算法实现(排序/查找/数组/链表/栈/队列/树/递归/海量数据处理/图/位图/Java版数据结构) 数据结构和算法作为程序员的基本功,一定得稳扎稳打的学习,我们常见的框架底层就是各类数据 ...
- 《数据结构与算法 C语言版》—— 3.8习题
本节书摘来自华章出版社<数据结构与算法 C语言版>一 书中的第3章,第3.8节,作者:徐凤生,更多章节内容可以访问云栖社区"华章计算机"公众号查看. 3.8习题 1名 ...
- 《数据结构与算法 C语言版》—— 2.5上机实验
本节书摘来自华章出版社<数据结构与算法 C语言版>一 书中的第2章,第2.5节,作者:徐凤生,更多章节内容可以访问云栖社区"华章计算机"公众号查看. 2.5上机实验 实 ...
- 《数据结构与算法 C语言版》—— 2.7习题
本节书摘来自华章出版社<数据结构与算法 C语言版>一 书中的第2章,第2.7节,作者:徐凤生,更多章节内容可以访问云栖社区"华章计算机"公众号查看. 2.7习题 1描 ...
- 江苏大学公交系统(数据结构与算法课设版C++)
江苏大学公交系统(数据结构与算法课设版C++) 目录 江苏大学公交系统(数据结构与算法课设版C++) (1)题目要求 (2)功能要求 (3) 结构 (4) 不足 附录 (1)题目要求 为2022级新生 ...
- Java数据结构和算法(第二版)
Java数据结构和算法(第二版) 下载地址 https://pan.baidu.com/s/112D5houIgu0eMs_i5o0Ujw 扫码下面二维码关注公众号回复 100066获取分享码 本书目 ...
- 数据结构与算法(Java版) | 本套系列教程的课程亮点和授课方式
接下来,在这一讲,我会花一点时间同同学们达成一个共识,就是我们这套系列教程在讲述的时候,究竟是以一种什么方式来讲述的.我希望,经过我的讲解之后,大家能够对我们这套系列教程的课程亮点和授课方式达成如下这 ...
- 《数据结构与算法》第二版-陈卫卫-陆军工程大学811数据结构教材 第1-2章 参考答案
<数据结构与算法>(第二版)陈卫卫-高等教育出版社 陆军工程大学811数据结构教材 第1-2章 参考答案 习题1.1 1.1-1 (1)名称.数量.特征.性质的 ...
- 数据结构——KMP算法(难懂版,但还是看看吧)
据说这个算法很难,起初看了<大话数据结构>,知道了这个算法,但是没看懂没理解,然后看其他博客,尽管博客上写着易懂,好理解,但我仍然看不懂,不理解,心里一直在口吐芬芳. 后来我看了几个版本的 ...
最新文章
- ERROR: Failed to resolve: com.github.CymChad:BaseRecyclerViewAdapterHelper:2.9.46
- 提高工作效率的 7 个 Vim 使用技巧!
- java component创建_spring--打印hello--注解component--自动创建对象
- c语言编程 验证用户名和密码是否正确(函数定义),《C语言程序设计教程》习题参考答案[精品资料].doc...
- hexo-Fluid主题使用手册
- linux设备驱动 注册 命令6,Linux设备驱动程序学习----6.模块的参数
- 家长工作比较忙,没有什么时间来带孩子,交给家里老人会养成一些坏习惯吗?
- Python3中使用json将字典转为json文件中的乱码问题
- Visual Stadio 2012创建WebApplication应用和运行赏析
- KMS激活工具原地址
- 贪心科技机器学习训练营(四)
- windows电脑如何设置定时关机?电脑设置定时关机的方法
- c语言数独思路介绍,【数独运算器】,关于数独的解题思路,已经改进。
- 计算机视觉、图像处理学习资料汇总(转)
- 小酷智慧地图3D导览v1.0.87打卡定位 地图打卡
- Django-路由Routers-SimpleRouter-DefaultRouter使用方法
- 图像处理之Hobject与Mat互转
- 读书百客:《郡斋雨中与诸文士燕集》赏析
- 实验二+108+曾宏宇
- qchart画完以后删除_国画山水精典学堂:泼墨山水画
热门文章
- 【VAR模型 | 时间序列】帮助文档:VAR模型的引入和Python实践(含源代码)
- 基于Aidlux平台的智慧交通AI安全算法实战
- 好男人就是好色的男人
- 国泰君安国际携手蔚来与宁德时代,助力新能源汽车电池产业创新发展做优、做强基础上突破发展,以金融支持实体经济
- DL/T645-2007 与DL/T645-1997协议解析对比
- 重庆高校在线平台的计算机基础,重庆高校在线开放课程建设数字课程建设标准.pdf...
- 从ftp、tftp自动获取文件的脚本
- C语言---指针笔试题
- Centos 7 安装 ORACLE 11g
- 进击的Android注入术