第四章不相交集数据结构Union-Find分析
合集下载
相关主题
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
33
这时, A[1] 中的元素可能小于存放在 它的一个子节点中的元素,于是用过程 SIFT-DOWN将A[1…n-1]转换成堆。 (调整堆) 接下来将 A[1] 和A[n-1]交换,并调整 数组A[1…n-2]成为堆。交换元素和调整 堆的过程一直重复,直到堆的大小变成1 为止,这时A[1] 是最小的。
12
过程:Sift-down 输入:数组H[1…n]和位于1和n之间的索引i 输出:下移H[i],以使它不小于子节点 1.done false 2.if 2i>n then exit //节点i是叶子,退出 3.repeat 4. i=2i //待比较位置左子节点 5. if i+1≤n and key(H[i+1])> key(H[i]) then i i+1 //存在右子节点,且右子节点最大 6. if key(H [i/2 ]) < key(H[i]) //与最大节点交换 then 互换H[i]和H [i/2 ] 7. Else done true 8. End if 9. Until 2i> n or done
或
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 的右孩子。
24
算法复杂度分析: (1)设T是一个完全二叉树,高度是k=logn 。
(2)设A[j]对应第i层的第j个结点,则调用 sift-down函数需要调用的次数应该最多是 k-i(注:k是树的高度)(自底而上扫描连接 的数据)。 (3)根据二叉树的理论知:二叉树的第i层最 多有2i个结点,则循环的上界应该为:
13
算法复杂度分析
Sift_down的复杂度分析: (1)以i结点和2i+1,2i+2的调整复杂度所 用时间为 (2)以2i+1或2i+2结点为父结点来调整的时间最坏是多 少? 发生调整的几率的树最多是2n/3,(即最底层恰好是 半满)。 则:
(1)
T (n) T (2n / 3) (1)
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)的空间。
21
看另外一个堆建立方法: 在Θ(n)的时间内,用n个元素来建 立一个堆,下面给出这种方法的实现细 节。
22
(1) 将堆H[1…n]的树的节点以自顶向下、 从左到右的方式从1到n编码。
(2)把一棵n个节点的几乎完全的二叉树 转换成堆H[1…n]。 (3) 从最后一个节点开始(编码为n 的 那个)到根节点(编码为1的节点),逐 个扫描所有的节点,根据需要,每一次将 以当前节点为根节点的子树转换成堆。
26
另外一种分析方法:
1、简单的分析: 每次调用SIFT-DOWN()函数的时间为 O(logn),一共应该有n/2次调用,所以 总的时间复杂度上线是O(nlogn)。 但是这个上限太高了。(这就意味 着几乎每一个结点都在执行sift_down() 函数行为)
27
分析一个确界:
1、 事实上,需要执行SIFT-DOWN()函数 的结点很少,而且对于不同的节点执行 SIFT-DOWN()函数的次数也是不同的, 这跟每一个结点所处的高度和所连接的 数据相关。
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
见例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]对应于数的叶子节点
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
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
(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元素的堆。
第四章
堆和不相交集数据结构
4.1 引言
4.2 堆 4.3 不相交集数据结构
1
4.2 堆
在许多算法中,需要支持下面两种运 算的数据结构:
插入元素和寻找最大元素。
支持这两种运算的数据结构称为优先队 列。 普通队列:那么寻找最大元素需要搜索整 个队列,开销较大;
2
排序数组: 那么插入运算就需要移动很多元素, 开销也较大. 优先队列的有效实现是使用一种称为 堆的简单数据结构。
11
Sift-down 算法: 前提: 当i≤n/2 , 设H[2i]和H[2i+1]中最大值为 max,如果H[i]中元素的键值变成了小于 max,这样就违反了堆的特性,树就不再 表示一个堆。 调整策略: 使H[i]渗到二叉树中适合它的位置上;
沿着这条路径的每一步,都将H[i]键值 和存储在它子节点中的两个键值里最大的 那个相比较。
ri
r2i
例如:
36
65 81 73 55 40 49 34 12
r2i+1
27Baidu Nhomakorabea
98
是堆
6
对数据结构支持下面的运算: Delete-max[H]:删除最大键值的数据项 Insert[H,x]:插入项x到堆H中 Delete[H,i]:从堆H中删除第i项 Makeheap[A]:将数组A转换成堆
3
定义 4.1 一个(二叉)堆是一个几乎完全的 二叉树,它的每个节点都满足堆的特性: 如果v和p(v)分别是节点和它的父节点, 那么存储在p(v)中的数据项键值不小于存储 在v中数据项的键值。(大顶堆)
4
数据结构中堆的定义: 堆是满足下列性质的数列{r1, r2, …,rn}:
ri r2i (小顶堆) ri r2i 1
n ) O (2 n ) O ( n ) h 1 2
30
4.2.3 堆排序 算法SELECTIONSORT(选择排序)的复 杂度: 用线性搜索来找最小值的时间需要用 Θ(n)的时间,算法就要用Θ(n2) 时间。 如何提高选择排序的复杂度呢,使用堆 是一个好的选择。
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])
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)。
16
算法4.2 DELETE//删除某个元素 输入:非空堆H[1…n]和位于1和n之间的索引i 输出:删除H[i]后的新堆H[1…n-1] 1. x H[i];y H[n] //删除x,最后一个数据暂存y 2. n= n-1 //数组大小减1 3. If i=n+1 then exit //删除的元素正好是最后 一个,不需要任何操作 4. H[i] y ;//不是最后一个则取回最后一个, 先放入H[i]中 5. If key(y)≥key(x) then SIFT-UP(H, i) 6. Else SIFT-DOWN(H, i) //根据需要进行调整 7.END IF
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 ]相比较。
堆的特性蕴含着:沿着每条从根到叶子 的路径,元素的键值以非升序排列。 ( 大顶堆)
7
有n个节点的堆T(一棵几乎完全的二叉 树),可以有一个数组H[1…n]用下面的方 式来表示。
T的根节点存储在H[1]中。 假设T的节点x存储在H[j]中,如果它有 左子节点,这个子节点存储在H[2j]中;如 果它也有右子节点,这个子节点存储在 H[2j+1]中。
,根据递推式求解: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)。