第四章不相交集数据结构Union-Find分析

合集下载

并查集解决元素分组及连通性问题

并查集解决元素分组及连通性问题

并查集解决元素分组及连通性问题并查集(Disjoint-set Union)是一种常用的数据结构,主要用于解决元素分组及连通性问题。

它将n个元素划分为若干个不相交的集合,每个集合通过一个代表来标识。

在该数据结构下,我们可以高效地进行合并集合和查询某个元素所属的集合操作。

一、并查集的基本原理并查集主要由两个操作组成:合并(Union)和查找(Find)。

其中,合并操作将两个集合合并为一个集合,查找操作则用于确定某个元素所属的集合。

在并查集中,每个元素都有一个指向父节点的指针,初始时每个元素自成一个集合,其父节点指向自己。

合并操作就是将两个元素所在集合的根节点合并为一个根节点,即将其中一个集合的根节点指向另一个集合的根节点。

查找操作通过递归地向上查找父节点,最终找到根节点,从而确定该元素所属的集合。

二、并查集的具体实现并查集的具体实现可使用数组或树结构。

数组实现简单直观,树结构实现更加高效。

以下是树结构实现的代码示例:```pythonclass UnionFind:def __init__(self, n):self.parent = list(range(n))def union(self, a, b):self.parent[self.find(a)] = self.find(b)def find(self, x):if self.parent[x] != x:self.parent[x] = self.find(self.parent[x])return self.parent[x]```在这个实现中,parent数组记录了每个元素的父节点,初始时每个元素的父节点为自己。

合并操作通过将某个元素的根节点指向另一个元素的根节点来实现。

查找操作递归地向上查找父节点,直到找到根节点。

三、应用场景举例1. 图的连通性判断:利用并查集可以判断图中的两个节点是否连通。

假设图中有n个节点,我们可以用并查集来维护这些节点的连通情况。

第四章不相交集数据结构Union-Find分析

第四章不相交集数据结构Union-Find分析
17
删除复杂度: 这些在算法DELETE中描述。有观察结论 3.4可知,堆树的高度是 log n ,所以从 一个大小为n的堆中删除一个元素所需要的 时间是O(log n)。
18
算法4.3 DELETEMAX//删除根节点 输入:堆H[1…n] 输出:返回最大值元素 x并将其从堆中删 除 1. x H[1] 2. DELETE(H,1) 3. Return x

ri r2i (大顶堆) ri r2i 1
例如:
{12, 36, 27, 65, 40, 34, 98, 81, 73, 55, 49} 是小顶堆 {12, 36, 27, 65, 40, 14, 98, 81, 73, 55, 49} 不是堆
5
若将该数列视作完全二叉树,则 r2i 是 ri 的左孩子; r2i+1 是 ri 的右孩子。
,根据递推式求解:O(logn)
14
算法4.1 INSERT 输入:堆H[1…n]和元素x 输出:新的堆H[1…n+1], x为其元素之 一 1. n= n+1{增加H的大小} 2. H[n] x 3. SIFT-UP(H, n)

15
插入 (1)先将堆大小加1,然后将x添加到H 的末尾,再根据需要,把x上移,直到满足 堆特性, (2)观察结论3.4可知,如果n是新堆的 大小,那么堆树的高度是 log n ,所以将 一个元素插入大小为n的堆中所需要的时间 是O(log n)。
31
给出数组A[1…n],将其中的元素用如 下方法以非降序有效排序。 step 1.首先将A变换成堆,并使其具有 这样的性质,每个元素的键值是该元素本 身,即key(A[i])= A[i],1 ≤i ≤n。

数据结构union函数 -回复

数据结构union函数 -回复

数据结构union函数-回复数据结构中的union函数是一种常见的操作,它用于合并两个集合或者查找两个元素所属的集合。

本文将详细介绍union函数的实现原理以及其在数据结构中的应用。

一、union函数概述在数据结构中,union函数的主要作用是将两个不相交的集合合并为一个集合,从而构建一个更大的集合。

具体来说,union函数的输入是两个集合以及它们的代表元素,输出是合并后的集合。

二、union函数的实现原理对于union函数的实现,常见的方法是使用并查集(disjoint set)数据结构。

并查集是一种用于处理不相交集合的数据结构,它支持合并集合和查询元素所属集合的操作。

在并查集中,每个集合用一棵树来表示,树的根节点指向自身,其他节点指向它的父节点。

每个集合由一个代表元素来表示,代表元素是根节点。

假设有两个集合A和B,分别由代表元素a和b表示,union函数的目标就是将集合A和B合并为一个集合。

具体而言,union函数的实现可以分为以下步骤:1. 首先,找到集合A和B的代表元素a和b。

2. 将代表元素b的父节点设置为a,即将集合B合并到集合A中。

3. 如果集合B的规模比集合A大,则更新集合A的代表元素为b。

可以通过路径压缩来优化union函数的性能。

路径压缩是一种在查找代表元素的过程中优化树形结构的方法,它通过将路径上的每个节点直接连接到根节点,减少树的高度,提高查找效率。

三、union函数的应用union函数在数据结构中有广泛的应用,下面分别介绍两个典型的应用场景。

1. 连通性问题在图论中,连通性问题是指判断两个节点是否存在路径连接的问题。

利用union函数可以高效地解决这个问题。

具体而言,可以使用一个并查集来表示图中的节点集合,每个节点用一个元素来表示。

当两个节点之间存在边时,可以使用union函数将它们所属的集合合并。

最后,利用find函数来判断两个节点是否属于同一个连通分量。

2. 集合合并在某些场合,需要将多个集合合并为一个更大的集合。

并查集__Union-Find_Sets_

并查集__Union-Find_Sets_

对于并查集来说,每个集合用一棵树表示。 对于并查集来说,每个集合用一棵树表示。 集合中每个元素的元素名分别存放在树的结 点中,此外, 点中,此外,树的每一个结点还有一个指向其 双亲结点的指针。 双亲结点的指针。 为此,需要有两个映射: 为此,需要有两个映射: 集合元素到存放该元素名的树结点间的对 应; 集合名到表示该集合的树的根结点间的对 应。 设 S1= {0, 6, 7, 8 },S2= { 1, 4, 9 },S3= { 2, 3, , , 5}
O(∑ i ) = O( n )
2 i =1
n
Union操作的加权规则 Union操作的加权规则
为避免产生退化的树, 为避免产生退化的树,改进方法是先判断两集合中元素 的个数, 的个数,如果以 i 为根的树中的结点个数少于以 j 为根 的树中的结点个数, 的树中的结点个数,即parent[i] > parent[j],则让 j 成为 , i 的双亲,否则,让i成为 的双亲。 的双亲,否则, 成为 的双亲。 成为j的双亲 此即Union的加权规则。 此即 的加权规则
下标
0
123ຫໍສະໝຸດ 4567
8
9
parent - 1
4
-1 2
-1 2
0
0 0
4
集合S1, S2和 S3的双亲表示
S1 U S2的可能的表示方法
®
const int DefaultSize = 10; class UFSets { //并查集的类定义 并查集的类定义 public: UFSets ( int s = DefaultSize ); ~UFSets ( ) { delete [ ] parent; } const UFSets & operator = ( UFSets const & Value ); void Union ( int Root1, int Root2 ); int Find ( int x ); void UnionByHeight ( int Root1, int Root2 ); private: int *parent; int size; };

算法与数据结构基础-合并查找(UnionFind)

算法与数据结构基础-合并查找(UnionFind)

算法与数据结构基础-合并查找(UnionFind)Union Find算法基础Union Find算法⽤于处理集合的合并和查询问题,其定义了两个⽤于并查集的操作:Find: 确定元素属于哪⼀个⼦集,或判断两个元素是否属于同⼀⼦集Union: 将两个⼦集合并为⼀个⼦集并查集是⼀种树形的数据结构,其可⽤数组或unordered_map表⽰:Find操作即查找元素的root,当两元素root相同时判定他们属于同⼀个⼦集;Union操作即通过修改元素的root(或修改parent)合并⼦集,下⾯两个图展⽰了id[6]由6修改为9的变化:图⽚来源Union Find算法应⽤Union Find可⽤于解决集合相关问题,如判断某元素是否属于集合、两个元素是否属同⼀集合、求解集合个数等,算法框架如下://261. Graph Valid Treebool validTree(int n, vector<pair<int, int>>& edges) {vector<int> num(n,-1);for(auto edge:edges){//find查看两点是否已在同⼀集合int x=find(num,edge.first);int y=find(num,edge.second);if(x==y) return false; //两点已在同⼀集合情况下则出现环//union让两点加⼊同⼀集合num[x]=y;}return n-1==edges.size();}int find(vector<int>&num,int i){if(num[i]==-1) return i;return find(num,num[i]); //id[id[...id[i]...]]}⼀些情况下为清晰和解偶会将Uinon Find实现为⼀个类,独⽴出明显的Union和Find两个操作。

18.用于不相交集合的数据结构

18.用于不相交集合的数据结构

(1)按秩合并
• 其思想是:合并时,使包含较少结点的树 的根指向包含较多结点的树的根。 • 秩:表示结点高度的一个上界。 • 因此,在按秩合并中,具有较小秩的根在 UNION操作中要指向具有较大秩的根。 • 即合并的时候将元素少的集合合并到元素 多的集合中,这样合并之后树的高度会相 对较小。
• 每个结点的秩是结点高度的一个上界,只 有在进行UNION操作时,而且进行合并操 作的两个集合的秩相同时,才会给最后的 根结点的秩+1。
算法导论
ACM/ICPC,编写我们的未来
18 用于不相交集合的数据结构
• 用于不相交集合的数据结构又称为并查集。 • 在很多的应用中(比如图的算法中经常使用 ,还有哈夫曼编码等),要将n个不同的元 素分成一组不相交的集合。 • 不相交集合上有两个重要的操作:
– 找出给定元素所属的集合 – 合并两个集合
3 不相交集合森林表示法
• 用有根树来表示集合,树中的每个结点都包 含集合的一个成员,每棵树表示一个集合。 • 每个成员仅指向其父结点,每棵树的根包含 了代表,并且是它自己的父结点。 • 例如:
• make_set(x) //把每一个元素初始化为一个 集合
– 创建一棵仅包含一个结点x的树 。
• find_set(x) //查找一个元素所在的集合
void make_Set(int x) { s[x]=new node; s[x]->rank=0; s[x]->p=s[x]; }
பைடு நூலகம்
node* find_Set(node* s) { if(s!=s->p) { s->p=find_Set(s->p); } return s->p; }

程序设计中常用的数据结构

程序设计中常用的数据结构

程序设计中常用的数据结构在程序设计中,数据结构是许多算法和程序的基础。

以下是程序设计中常用的几种数据结构:1. 数组(Array):数组是一组有序的数据元素,可以通过索引值来访问和修改数组中的元素。

数组的主要优点是支持随机访问,但插入和删除操作的效率相对较低。

2. 栈(Stack):栈是一种后进先出(LIFO)的数据结构,只能从栈顶插入和删除数据。

栈的主要应用场景包括函数调用、表达式求值和内存分配等。

3. 队列(Queue):队列是一种先进先出(FIFO)的数据结构,只能从队列尾插入数据并从队列头删除数据。

队列的主要应用场景包括广度优先搜索和任务调度等。

4. 链表(Linked List):链表是一种递归的数据结构,由若干个节点组成,每个节点包含当前元素的值和指向下一个节点的指针。

链表支持快速插入和删除操作,但访问特定位置的元素需要顺序查找,效率相对较低。

5. 哈希表(Hash Table):哈希表是一种基于哈希函数实现的数据结构,可以快速地插入、删除和查找元素。

在哈希表中,元素的存储位置是通过哈希函数计算得到的,因此访问特定位置的元素效率很高。

6. 树(Tree):树是一种层次结构,由若干个节点和连接这些节点的边组成。

常见的树形数据结构包括二叉搜索树、红黑树、AVL 树和 B 树等。

树的主要应用场景包括搜索、排序和存储等。

7. 图(Graph):图是由一组节点和连接这些节点的边组成的数据结构。

图常用来表示实体之间的关系,如社交网络、路线图等。

在计算机科学中,图的主要应用包括最短路径算法、网络流等。

8. 堆(Heap):堆是一种特殊的树形数据结构,它满足某些特定的性质。

被称为“最小堆”的堆中,每个父节点的值都小于或等于其子节点的值,而被称为“最大堆”的堆中,每个父节点的值都大于或等于其子节点的值。

堆可以用来实现优先队列和堆排序等算法。

9. 字典树(Trie):字典树是一种用于字符串处理的树形数据结构。

数据结构 第4章

数据结构 第4章



例子

假设要建立一个地址区间长度为13的哈希表,哈希函数为 H(key) = Ord(关键字第一个字母)-1)/2 其中函数Ord求字母在字母表中的序号。例如,字母A在 字母表中的序号为1, Ord(‘A’)=1。 现将关键字依次为Zhao, Qian, Sun, Li, Wu, Chen, Han的 7 个记录插入该哈希表。


例子

例如构造一个数据元素个数n = 60,哈希地址空间长度m = 100 的哈希表。 对关键字分析发现,关键字的第 1、2、3、6位取值比较集中,不 宜作为哈希地址,
…… 8 8 8 8 8 8 8 8 1 1 2 1 2 2 1 1 3 3 7 3 7 7 3 3 1 2 3 4 0 7 8 7 6 9 3 6 4 1 6 8 6 6 8 6 2 0 7 2 3 1 3 6 2 5 8 4 5 8 2
数据结构
广东工业大学 计算机学院
第4章 哈希表
第4章 哈希表


4.1 哈希表的概念 4.2 哈希函数的构造方法

4.2.1 直接定址法 4.2.2 除留余数法 4.2.3 数字分析法 4.2.4 折叠法 4.2.5 平方取中法 4.3.1 开放定址法 4.3.2 链地址法 链地址哈希表的实现 开放定址哈希表的实现
移位叠加 0040 1108 1053 0216 + 9891 (1)2308

Z形叠加 0040 8011 1053 6120 + 9891 (2)5115
4.2.5 平方取中法


平方取中法先取关键字的平方,然后根据哈希表地址区 间长度m的大小,选取平方数的中间若干位作为哈希地 址。通过取平方扩大关键字之间的差别,而平方值的中 间若干位和这个数的每一位都相关,使得不同关键字的 哈希函数值分布较为均匀,不易产生冲突。 设哈希表地址区间长度为1000,可取关键字平方值的中 间三位。
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

31
给出数组A[1…n],将其中的元素用如 下方法以非降序有效排序。 step 1.首先将A变换成堆,并使其具有 这样的性质,每个元素的键值是该元素本 身,即key(A[i])= A[i],1 ≤i ≤n。
32

step 2.由于A中各项的最大值存储在 A[1]中,可以将A[1] 和A[n]交换,使得 A[n]是数组中最大元素。 (排序:将数据依大小,顺序放在数组 中,最后一个是最大的,故交换A[1]和 A[n])

34
算法4.5 HEAPSORT 输入:n个元素的数组A[1…n] 输出:以非降序排列的数组A 1.MAKEHEAP(A) 2. for j n downto 2//j从n依次递减 3. 互换A[1] A[j] 4. SIFT-DOWN(A[1…j-1],1) 5. end for 定理4.2 算法HEAPSORT对n个元素排序 要用O(n log n)时间和Θ(1)的空间。
3
定义 4.1 一个(二叉)堆是一个几乎完全的 二叉树,它的每个节点都满足堆的特性: 如果v和p(v)分别是节点和它的父节点, 那么存储在p(v)中的数据项键值不小于存储 在v中数据项的键值。(大顶堆)
4
数据结构中堆的定义: 堆是满足下列性质的数列{r1, r2, …,rn}:
ri r2i (小顶堆) ri r2i 1
17
删除复杂度: 这些在算法DELETE中描述。有观察结论 3.4可知,堆树的高度是 log n ,所以从 一个大小为n的堆中删除一个元素所需要的 时间是O(log n)。
18
算法4.3 DELETEMAX//删除根节点 输入:堆H[1…n] 输出:返回最大值元素 x并将其从堆中删 除 1. x H[1] 2. DELETE(H,1) 3. Return x
,根据递推式求解:O(logn)
14
算法4.1 INSERT 输入:堆H[1…n]和元素x 输出:新的堆H[1…n+1], x为其元素之 一 1. n= n+1{增加H的大小} 2. H[n] x 3. SIFT-UP(H, n)

15
插入 (1)先将堆大小加1,然后将x添加到H 的末尾,再根据需要,把x上移,直到满足 堆特性, (2)观察结论3.4可知,如果n是新堆的 大小,那么堆树的高度是 log n ,所以将 一个元素插入大小为n的堆中所需要的时间 是O(log n)。
log n
n ) h 1 2
( 1)
29
数论上存在如下等式成立:
x kx 2 (1 x ) k 0
k

设x=1/2带入(1)的右边的和式
h 2 2 k 2 1 2 (1 ) h 0 2

1
O( n
h 0
log n
(k i)2
i 1
k 1
i
2 (k ) 2 (k 1) ... 2 (1) 2n
0 1
k 1
25
观察sift-down函数,每一次循环过程,包含有 两个if语句,故最多两次元素比较,故元素比较的总 次数上界是2*2n=4n; 观察下界:调用sift-down函数至少要执行一次 循环,所以元素的最小比较次数应为: 2*n/2>=n-1。 定理4.1 算法MAKEHEAP用来构造一个n个元素 的堆,令C(n)为执行该算法的元素比较次数,那 么n-1≤ C(n) <4n。因此,算法需要Θ(n) 时间和 Θ(1) 空间构造一个n元素的堆。
21
看另外一个堆建立方法: 在Θ(n)的时间内,用n个元素来建 立一个堆,下面给出这种方法的实现细 节。

22
(1) 将堆H[1…n]的树的节点以自顶向下、 从左到右的方式从1到n编码。
(2)把一棵n个节点的几乎完全的二叉树 转换成堆H[1…n]。 (3) 从最后一个节点开始(编码为n 的 那个)到根节点(编码为1的节点),逐 个扫描所有的节点,根据需要,每一次将 以当前节点为根节点的子树转换成堆。
10
过程:Sift-up算法 输入:数组H[1…n]和位于1和n之间的索引i 输出:上移H[i],以使它不大于父节点 1.done false 2.if i=1 then exit //节点i是根 3.repeat 4. if key(H[i])>key(H [i/2 ]) then 互换H[i]和H [i/2]//对比交换 5. Else done true 6. i i/2 7. Until i=1 or done
第四章
堆和不相交集数据结构
4.1 引言
4.2 堆 4.3 不相交集数据结构
1
4.2 堆
在许多算法中,需要支持下面两种运 算的数据结构:
插入元素和寻找最大元素。
支持这两种运算的数据结构称为优先队 列。 普通队列:那么寻找最大元素需要搜索整 个队列,开销较大;
2

排序数组: 那么插入运算就需要移动很多元素, 开销也较大. 优先队列的有效实现是使用一种称为 堆的简单数据结构。
24

算法复杂度分析: (1)设T是一个完全二叉树,高度是k=logn 。
(2)设A[j]对应第i层的第j个结点,则调用 sift-down函数需要调用的次数应该最多是 k-i(注:k是树的高度)(自底而上扫描连接 的数据)。 (3)根据二叉树的理论知:二叉树的第i层最 多有2i个结点,则循环的上界应该为:
11
Sift-down 算法: 前提: 当i≤n/2 , 设H[2i]和H[2i+1]中最大值为 max,如果H[i]中元素的键值变成了小于 max,这样就违反了堆的特性,树就不再 表示一个堆。 调整策略: 使H[i]渗到二叉树中适合它的位置上;
沿着这条路径的每一步,都将H[i]键值 和存储在它子节点中的两个键值里最大的 那个相比较。
n ) O (2 n ) O ( n ) h 1 2
30
4.2.3 堆排序 算法SELECTIONSORT(选择排序)的复 杂度: 用线性搜索来找最小值的时间需要用 Θ(n)的时间,算法就要用Θ(n2) 时间。 如何提高选择排序的复杂度呢,使用堆 是一个好的选择。
33
这时, A[1] 中的元素可能小于存放在 它的一个子节点中的元素,于是用过程 SIFT-DOWN将A[1…n-1]转换成堆。 (调整堆) 接下来将 A[1] 和A[n-1]交换,并调整 数组A[1…n-2]成为堆。交换元素和调整 堆的过程一直重复,直到堆的大小变成1 为止,这时A[1] 是最小的。
13
算法复杂度分析
Sift_down的复杂度分析: (1)以i结点和2i+1,2i+2的调整复杂度所 用时间为 (2)以2i+1或2i+2结点为父结点来调整的时间最坏是多 少? 发生调整的几率的树最多是2n/3,(即最底层恰好是 半满)。 则:
(1)
T (n) T (2n / 3) (1)
28
一个n元素的堆的高度是 ,在任意的高度h上,最多有结点数
log n
h 1 n / 2
设SIFT-DOWN()在h层上的作用时间是O(h),则 应该有一个上确界:
log n

h 0
n O(h) O(n h 1 2 h 0
19
删除最大值复杂度分析: (1) 在堆中返回最大键值元素需要Θ(1) 的时间,因为这个元素是树的根节点。 (2)修复堆需要一定的时间。 显然,这项运算的时间复杂性就是删除 运算的时间复杂性,即O(log n)。
20
4.2.2 创建堆 给出一个A[1…n],创建一个包含这些元 素的堆是容易的: 从空的堆开始,不断插入一个元素,直 到A完全被转移到堆中为止。 因为插入第j个键值用时O(log j),因此 用此方法创建堆栈的时间复杂性是O(n log n)。
8

元素H[j]的父节点如果不是根节点,则存 储在H[ j/2 ]中。
9
4.2.1 堆上的运算 下面是两个辅助运算 Sift-up 运算 : 前提:假定对于某个i>1, H[i]变成了键值 大于它父节点键值的元素,非堆结构。 处理方案:调整破坏堆的数据的位置; 实际方法: 沿着从H[i]的根节点的唯一一条路经, 把H[i]移到适合它的位置上。在沿着路径的 每一步上,都将H[i]键值和它父节点的键值 H[i/2 ]相比较。
见例4.3 创建堆的例子(手写)
23
算法MAKEHEAP构建了一个堆,其 数据项是存储在数组A[1…n]中的元素。 算法 4.4 MAKEHEAP 输入:n个元素的数组A[1…n] 输出: A[1…n]转换成堆 1. For i n/2 downto 1 2. SIFT-DOWN(A, i) 3. End for //注:叶子节点不需要处理,所以必须 从n/2开始处理,A[n/2+1], A[n/2+2],..A[n]对应于数的叶子节点

堆的特性蕴含着:沿着每条从根到叶子 的路径,元素的键值以非升序排列。 ( 大顶堆)
7
有n个节点的堆T(一棵几乎完全的二叉 树),可以有一个数组H[1…n]用下面的方 式来表示。
T的根节点存储在H[1]中。 假设T的节点x存储在H[j]中,如果它有 左子节点,这个子节点存储在H[2j]中;如 果它也有右子节点,这个子节点存储在 H[2j+1]中。
ri
r2i
例如:
36
65 81 73 55 40 49 34 12
r2i+1
27
98
是堆
6
对数据结构支持下面的运算: Delete-max[H]:删除最大键值的数据项 Insert[H,x]:插入项x到堆H中 Delete[H,i]:从堆H中删除第i项 Makeheap[A]:将数组A转换成堆
相关文档
最新文档