斐波那契堆详解+摊还分析(附带注释C代码)

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

斐波那契堆详解+摊还分析(附带注释C代码)
题注:此代码为浙江⼤学 ADS 课程使⽤,请勿抄袭作业。

斐波那契堆是⼀种可合并堆,⽀持以下5中操作:
MAKE-HEAP() : 创建和返回⼀个新的不含任何元素的堆
INSERT(H, x) : 将⼀个已填⼊关键字的元素 x 插⼊堆 H 中
MINIMUM(H) : 返回⼀个指向堆 H 中具有最⼩关键字元素的指针
EXTRACT-MIN(H) : 从堆 H 中删除最⼩关键字的元素,并返回⼀个指向该元素的指针
UNION(H1, H2) : 创建并返回⼀个包含堆 H1 和 H2 所有元素的新堆。

堆 H1 和堆 H2 由这⼀操作 “销毁”
除此之外,斐波那契堆还⽀持以下两种操作:
DECREASE-KEY(H, x, k) : 将堆 H 中元素 x 的关键字赋予新值 k。

假定新值 k 不⼤于当前关键字
DELETE(H, x) : 从堆 H 中删除元素 x
1. 斐波那契堆结构
⼀个斐波那契堆是具有最⼩堆序的有根树的集合。

每个有根树都具有最⼩堆的性质。

通过 H->min 来访问斐波那契堆 H。

该结点指向具有最⼩关键字的树的根节点。

在斐波那契堆中,所有树的根都⽤其 left 和 right 指针链成⼀个环形的双链表,该双链表称为斐波那契堆的根链表。

x 的孩⼦链表也是⽤环形链表存储的, x->child 指向随机的⼀个孩⼦,兄弟间顺序随意。

结点度数为孩⼦数。

typedef struct FibNode *HeapNode;
typedef struct FibNode *Position;
typedef struct FibHeap *FibQueue;
struct FibHeap {
// 指向堆中最⼩元素
Position min;
// 堆中的结点个数
int NodeNum;
};
struct FibNode {
// 环形链表,分别指向左右⼉⼦
Position LeftSibling;
Position RightSibling;
// 指向双亲和其中⼀个⼉⼦
Position Parent;
Position FirstChild;
// 结点标记
bool Mark;
// 元素值
int Element;
// 结点度数
int Degree;
};
势函数
为了之后的摊还分析,我们定义斐波那契堆 H 的势函数
Φ(H)=t(H)+2m(H)
t(H) 表⽰ H 中根链表中树的数⽬,m(H) 表⽰ H 中已标记的结点数⽬
最⼤度数
对于摊还分析,我们先假定, n 个结点的斐波那契堆任何结点度数的上界为D(n) , 其中D(n)=O(lgn)
2.可合并堆操作
创建新堆
// 创建⼀个新的斐波那契堆
FibQueue MAKE_HEAP()
{
FibQueue H = new FibHeap;
H->min = NULL;
H->NodeNum = 0;
return H;
}
插⼊⼀个结点
插⼊操作很⽅便,只需要在根链表中插⼊⼀个结点。

如图所⽰,直接把 21 插⼊到 3 的左边。

// X 插⼊ H 的 RootList
void HEAP_INSERT_ROOT(FibQueue H, HeapNode X)
{
// 更新左兄弟的右指针
H->min->LeftSibling->RightSibling = X;
// X 左兄弟为 H->min 左兄弟
X->LeftSibling = H->min->LeftSibling;
// 更新 H->min 左兄弟
H->min->LeftSibling = X;
// X 右兄弟 H->min
X->RightSibling = H->min;
}
// X 插⼊ Fib H
void HEAP_INSERT(FibQueue H, HeapNode X)
{
// 初始化新插⼊的结点
X->Degree = 0;
X->Parent = NULL;
X->FirstChild= NULL;
X->Mark = false;
// 若堆中没有结点,插⼊第⼀个
// 只有 X, 指向⾃⼰即可
X->LeftSibling = X;
X->RightSibling = X;
// 只有X, 所以 H->min = X
H->min = X;
}
// 已有结点
else {
// 先把 X 插⼊循环链表中 (H->min 左边即可)
HEAP_INSERT_ROOT(H, X);
// 检查并更新 H->min
if(X->Element < H->min->Element)
H->min = X;
}
// 插⼊⼀个结点,结点数 + 1
H->NodeNum++;
}
摊还分析
设H′是操作后的堆,则有t(H′)=t(H)+1,M(H′)=M(H), 并且势的增加量为:
((t(H)+1)+2m(H))−(t(H)+2m(H))=1
实际代价为O(1), 所以摊还代价为O(1)+1=O(1)
斐波那契堆合并
// 合并 X 和 Y 的根链表
void Concatenate(HeapNode X, HeapNode Y)
{
// 若为空,不需要合并
if(X == NULL || Y == NULL)
return;
X->RightSibling->LeftSibling = Y;
Y->RightSibling->LeftSibling = X;
X->RightSibling = Y->RightSibling;
Y->RightSibling = X->RightSibling;
}
// 合并堆 H1, H2
void HEAP_UNION(FibQueue H1, FibQueue H2)
{
// 创建⼀个新堆
FibQueue H = MAKE_HEAP();
// H 指向 H1
H->min = H1->min;
// 把 H2 的 RootList 和 H1 合并
Concatenate(H->min, H2->min);
return H;
}
抽取最⼩结点
⾸先把最⼩结点 X 的⼉⼦都上提到根链表中,之后把 X 从链表中移除。

先把 H->min 随意指向⼀个根节点,之后的 CONSOLIDATE 和合并根结点并找到 H->min。

// X 从 H RootList 移除
void HEAP_REMOVE_ROOT(HeapNode X)
{
// X 的左⼉⼦指向右⼉⼦
X->LeftSibling->RightSibling = X->RightSibling;
// X 的右⼉⼦指向左⼉⼦
X->RightSibling->LeftSibling = X->LeftSibling;
}
HeapNode HEAP_EXTRACT_MIN(FibQueue H)
{
// ⾸先把 Z 的所有⼉⼦上移到 H 的 RootList 中
HeapNode Z = H->min;
HeapNode X, Y;
if(Z != NULL) {
X = Z->FirstChild;
// 遍历 Z 的⼉⼦
while(Z->Degree--) {
// X 插⼊后右兄弟改变,提前记录
Y = X->RightSibling;
// 插⼊ RootList
HEAP_INSERT_ROOT(H, X);
// PrintRoot(H);
// 更新双亲
X->Parent = NULL;
// 下⼀个兄弟
X = Y;
}
// 将 Z 从 RootList 移除
HEAP_REMOVE_ROOT(Z);
// Z 指向⾃⼰,说明移除后堆为空
if(Z == Z->RightSibling)
H->min = NULL;
else {
// 先把 H->min 随意指向⼀个结点
H->min = Z->RightSibling;
// PrintRoot(H);
// 调整堆结构
HEAP_CONSOLIDATE(H);
}
// 移除结点,结点数 - 1
H->NodeNum--;
}
return Z;
}
合并根链表
这是斐波那契堆最关键的⼀步,通过合并使时间复杂度降低。

合并根链表的过程为重复执⾏以下步骤,知道根链表中每⼀个根都有不同度数。

1. 在根链表中找到两个具有相同度数的根 x 和 y, 不失⼀般性,假定x.key≤y.key
2. 把 y 链接到 x: 根链表中移除 y, 调⽤ HEAP-LINK, 使 y 成为 x 的孩⼦。

x.degree + 1,清除 y 的标记(y 的作⽤之后会讲到)使⽤辅助数组么 A[0, ... D(n)]
void MAKECHILD(HeapNode X, HeapNode Y)
{
X->Degree++;
// 找到 X 的第⼀个⼉⼦, 把 Y 插⼊到第⼀个⼉⼦左边
HeapNode Z = X->FirstChild;
// X 没有⼉⼦
if(Z == NULL) {
Y->LeftSibling = Y;
Y->RightSibling = Y;
X->FirstChild = Y;
}
// 已经有⼉⼦
else {
// 更新左兄弟的右兄弟
Z->LeftSibling->RightSibling = Y;
// 更新 Y 的左兄弟
Y->LeftSibling = Z->LeftSibling;
// 更新 Z 的左兄弟
Z->LeftSibling = Y;
// 更新 Y 的右兄弟
Y->RightSibling = Z;
}
// 更新 Y 的双亲
Y->Parent = X;
}
void HEAP_LINK(FibQueue H, HeapNode X, HeapNode Y)
{
// 把 Y 从 RootList 移除
// Y 作为 X 的⼉⼦, X 度数 + 1
MAKECHILD(X, Y);
Y->Mark = false;
}
// 调整后的 FibHeap 满⾜ RootList 中没有相同度数的根
void HEAP_CONSOLIDATE(FibQueue H)
{
// 辅助数组 A[i] 指向⼀个 degree 为 i 的根
// Degree 的上界为 log n
int num = (log(H->NodeNum) + 1) * 2;
for(int i = 0; i < num; i++)
A[i] = NULL;
HeapNode M = H->min;
HeapNode R = M->RightSibling;
HeapNode X, Z;
// 遍历 RootList
do {
X = R;
Z = X;
R = X->RightSibling;
int d = X->Degree;
// 存在 degree 相同的结点
while(A[d] != NULL) {
HeapNode Y = A[d];
// X 为⼩结点,合并在堆顶
if(X->Element > Y->Element)
NodeSwap(&X, &Y);
// 合并 X, Y
HEAP_LINK(H, X, Y);
// 合并后的度数为 d + 1
A[d] = NULL;
d++;
}
// 循环结束, A[d] = X
A[d] = X;
//PrintRoot(H);
} while(Z != M);
H->min = NULL;
// 把 A[i] 重新放⼊ RootList 中
for(int i = 0; i < num; i++) {
HeapNode X = A[i];
// A[i] 存在,插⼊ RootList
if(X != NULL)
// 插⼊空 RootList
if(H->min == NULL) {
// 只有 X, 指向⾃⼰即可
X->LeftSibling = X;
X->RightSibling = X;
// 只有X, 所以 H->min = X
H->min = X;
}
// 已有结点
else {
// 先把 X 插⼊循环链表中 (H->min 左边即可)
HEAP_INSERT_ROOT(H, X);
// 检查并更新 H->min
if(X->Element < H->min->Element)
H->min = X;
}
}
}
摊还分析
在 CONSOLIDATE 中,根链表的⼤⼩最⼤为D(n)+t(H)−1。

其中 for 循环遍历了根链表,⽽ while循环的合并操作最多会合并根结点个数次 (每次合并结点个数 - 1) ,因此抽取最⼩结点的实际⼯作量为O(D(n)+t(H))
抽取前的势为t(H)+2m(H) , 因为最多有 D(n) + 1 个结点留下且没有任何结点被标记,所以操作后最⼤的势为D(n)+1+2m(H)
摊还代价最多为:
O(D(n)+t(H))+((D(n)+1)+2m(H))−(t(H)+2m(H))
=O(D(n))+O(t(H))−t(H)
=O(D(n))
3.关键字减值与删除
关键字减值
找到 X 后,把 X 和⽗亲 Y 断开,并把 X 加⼊到根链表中。

这⾥的关键在于级联操作, X 断开后, Y 的标记可能改变,导致 Y 也要继续断开,以维护斐波那契堆。

// 切断 Y 和 X
{
// 把 X 从 Y 的⼉⼦列表中移除, Y.degree - 1
HEAP_REMOVE_CHILD(X, Y);
// 把 X 加⼊到 RootList 中
HEAP_INSERT_ROOT(H, X);
// X 为根
X->Parent = NULL;
X->Mark = false;
}
void HEAP_DECREASE_KEY(FibQueue H, HeapNode X, int k)
{
// 值只能减⼩,不能增⼤
if(k > X->Element) {
printf("new key is greater than current key\n");
return;
}
X->Element = k;
HeapNode Y = X->Parent;
// 如果 Y 存在并且 X 需要上移
if(Y != NULL && X->Element < Y->Element) {
CUT(H, X, Y);
CASCADING_CUT(H, Y);
}
// 更新 H->min
if(X->Element < H->min->Element)
H->min = X;
}
mark 标记
使⽤ mark 属性记录结点的状态。

假定下⾯的步骤已经发正在了 X 上
1. 在某个时刻, X 是根。

2. 然后 X 被链接到另⼀个结点(成为孩⼦结点)
3. 然后 X 有两个孩⼦被切断操作切除。

⼀旦失掉第⼆个孩⼦,就切断 X 与⽗结点的链接,使成为新的根。

这就是CASCADING(级联)的意义所在。

因此每当 X 被移⾄根上时, mark 为 false , 回到状态⼀。

/* 判断 Y 是否要和⽗亲切断
1. Y 曾经作为根
2. Y 之后成为了孩⼦结点(因为 Z 是 Y 的⽗亲)
3. 然后 Y 的两个孩⼦被切除了 (切除第⼀个孩⼦ Mark 为 True, 切除第⼆个孩⼦ Y 和 Z 分离)
*/
void CASCADING_CUT(FibQueue H, HeapNode Y)
{
HeapNode Z = Y->Parent;
if(Z != NULL)
// 切断了⼀个⼉⼦,标记为 true, 为下次切断做准备
if(Y->Mark == false)
Y->Mark = true;
// 符合切断条件,递归向上继续切断
else {
//
CUT(H, Y, Z);
CASCADING_CUT(H, Z);
}
}
摊还分析
假定⼀次 DECREASE-KEY 调⽤了 c 次 CASCADING, 则实际代价为O(c)
接下来计算势的变化。

除了最后⼀次级联,其余每次都会使⼀个结点的标记变为 false, 因此最多有m(H)+c−2 个被标记的结点(减少了c−1 个,最后⼀次可能会增加⼀个,
cut 的结点可能为 false) 。

此时的斐波那契堆包含t(H)+c棵树。

所以势的变化最多为
((t(H)+c)+2(m(H)−c+2))−(t(H)+2m(H))=4−c
所以斐波那契堆的摊还代价⾄多是
O(c)+4−c=O(1)
删除结点
// 删除堆中结点 X
void HEAP_DELETE(FibQueue H, HeapNode X)
{
HEAP_DECREASE_KEY(H, X, -INFINITY);
HEAP_EXTRACT_MIN(H);
}
代价为 DECREASE-KEY 和 EXTRACT-MIN 时间之和,所以 DELETE摊还时间为O(lgn)
4.最⼤度数的界
我们之前还遗留了⼀个最重要的证明,即任意结点的度数的上界D(n)为O(lgn)
特别地,要证明D(n)<=⌊log nϕ⌋, 这⾥ϕ是黄⾦分割率,
ϕ=(1+√5)/2=1.61803...
关键在于定义 size(x) 为以 X 为根的⼦树中包括 X 本⾝的结点个数。

证明 size(x) 是 x.degree 的幂。

引理 1
设x是斐波那契堆任意结点,并假定x.degree≥k。

设y1,y2,⋯y k表⽰x的孩⼦,并以链⼊的先后顺序排序,则y1.degree≥0,且对于i=1,2,3,⋯证明
显然, y1.degree≥0,对于i≥2,注意到y i⼀定在y1,y2,⋯y i−1后键⼊,因此有x.degree≥i−1。

⽽结点y成为 X ⼉⼦的条件是x.degree=y i.degree,所以此时⼀定有
y i≥i−1, 之后结点y最多失去⼀个孩⼦(失去两个孩⼦被切除)。

综上,y i.degree≥i−2
引理 2
对于斐波那契数列:
F=0,如果k=0 x,如果k=1 F k−1+F k−2如果k≥2
有另⼀种表⽰⽅法
F k+2=1+
k ∑i=0F i
证明
做归纳假设
F k+1=1+k−1∑i=0F i
F k+2=F k+F k+1=F k+(1+k−1

i=0F i)=1+
k

i=0F i
引理 3
对于所有整数k≥0,斐波那契的第k+2 个数满⾜F k+2≥ϕk
证明
假定对于i=0,1,⋯,k−1,有F i+2>ϕi
于是
F k+2=F k+1+F k
≥ϕk+1+ϕk−2
=ϕk−2(ϕ+1)
=ϕk−2⋅ϕ2
=ϕk
引理 4
设 x 是斐波那契堆中的任意结点, 并设 k = x.degree,则有size(x)≥F k+2≥ϕk,
证明
显然,s0=1,s1=2。

s k最⼤为size(x) , s k的值递增。

size(x)≥s k≥2+
k

i=2s y
i
.degree≥2+
k

i=2s i−2
利⽤s k≥F k+2归纳
s k≥2+
k

i=2s i−2≥2+
k

i=2F i=1+
k

i=0F i
=F k+2
≥ϕk 推论
⼀个 n 个结点的斐波那契堆中任意结点的最⼤度数 D(n) 为 O(lgn)
证明
n≥size(x)≥ϕk , 取对数,k≤log nϕ, 所以任意结点的最⼤度数 D(n) 为 O(lgn) 5. 完整代码
#include <iostream>
#include <cmath>
using namespace std;
typedef struct FibNode *HeapNode;
typedef struct FibNode *Position;
typedef struct FibHeap *FibQueue;
struct FibHeap {
// 指向堆中最⼩元素
Position min;
// 堆中的结点个数
int NodeNum;
};
struct FibNode {
// 环形链表,分别指向左右⼉⼦
Position LeftSibling;
Position RightSibling;
// 指向双亲和其中⼀个⼉⼦
Position Parent;
Position FirstChild;
bool Mark;
// 元素值
int Element;
// 结点度数
int Degree;
};
HeapNode *A;
{
{
cout<<X->Element<<endl;
cout<<X->LeftSibling->Element<<" "<<X->RightSibling->Element<<endl; cout<<endl;
}
void PrintRoot(FibQueue H)
{
//cout<<"The RootList is:"<<endl;
HeapNode M = H->min;
HeapNode X = M;
do {
X = X->RightSibling;
PrintNode(X);
} while(X != M);
}
// 创建⼀个新的斐波那契堆
FibQueue MAKE_HEAP()
{
FibQueue H = new FibHeap;
H->min = NULL;
H->NodeNum = 0;
return H;
}
// X 插⼊ H 的 RootList
void HEAP_INSERT_ROOT(FibQueue H, HeapNode X)
{
// 更新左兄弟的右指针
H->min->LeftSibling->RightSibling = X;
// X 左兄弟为 H->min 左兄弟
X->LeftSibling = H->min->LeftSibling;
// 更新 H->min 左兄弟
H->min->LeftSibling = X;
// X 右兄弟 H->min
X->RightSibling = H->min;
}
// X 从 H RootList 移除
void HEAP_REMOVE_ROOT(HeapNode X)
{
// X 的左⼉⼦指向右⼉⼦
X->LeftSibling->RightSibling = X->RightSibling;
// X 的右⼉⼦指向左⼉⼦
X->RightSibling->LeftSibling = X->LeftSibling;
}
// 把 X 从 Y 的⼉⼦列表中移除
void HEAP_REMOVE_CHILD(HeapNode X, HeapNode Y)
{
Y->Degree--;
// 防⽌ Y 的 Child 连到 X 的情况
Y->FirstChild = X->RightSibling;
// 把 X 从链表中移除
HEAP_REMOVE_ROOT(X);
// Y 没有⼉⼦了
if(!Y->Degree)
Y->FirstChild = NULL;
}
// X 插⼊ Fib H
void HEAP_INSERT(FibQueue H, HeapNode X)
{
// 初始化新插⼊的结点
X->Degree = 0;
X->Parent = NULL;
X->FirstChild= NULL;
X->Mark = false;
// 若堆中没有结点,插⼊第⼀个
if(H->min == NULL) {
// 只有 X, 指向⾃⼰即可
X->LeftSibling = X;
X->RightSibling = X;
// 只有X, 所以 H->min = X
H->min = X;
}
// 已有结点
else {
// 先把 X 插⼊循环链表中 (H->min 左边即可)
HEAP_INSERT_ROOT(H, X);
// 检查并更新 H->min
if(X->Element < H->min->Element)
H->min = X;
}
// 插⼊⼀个结点,结点数 + 1
H->NodeNum++;
}
// 交换 X, Y
void NodeSwap(HeapNode *X, HeapNode *Y)
{
HeapNode Temp;
Temp = *X;
*X = *Y;
*Y = Temp;
}
// 把 Y 合并成 X 的⼉⼦
void MAKECHILD(HeapNode X, HeapNode Y)
{
X->Degree++;
// 找到 X 的第⼀个⼉⼦, 把 Y 插⼊到第⼀个⼉⼦左边
HeapNode Z = X->FirstChild;
// X 没有⼉⼦
if(Z == NULL) {
Y->LeftSibling = Y;
Y->RightSibling = Y;
X->FirstChild = Y;
}
// 已经有⼉⼦
else {
// 更新左兄弟的右兄弟
Z->LeftSibling->RightSibling = Y;
// 更新 Y 的左兄弟
Y->LeftSibling = Z->LeftSibling;
// 更新 Z 的左兄弟
Z->LeftSibling = Y;
// 更新 Y 的右兄弟
Y->RightSibling = Z;
}
void TEST(HeapNode X, HeapNode Y)
{
X->LeftSibling = Y;
}
void HEAP_LINK(FibQueue H, HeapNode X, HeapNode Y) {
// 把 Y 从 RootList 移除
// Y 作为 X 的⼉⼦, X 度数 + 1
MAKECHILD(X, Y);
Y->Mark = false;
}
// 调整后的 FibHeap 满⾜ RootList 中没有相同度数的根void HEAP_CONSOLIDATE(FibQueue H)
{
// 辅助数组 A[i] 指向⼀个 degree 为 i 的根
// Degree 的上界为 log n
int num = (log(H->NodeNum) + 1) * 2;
for(int i = 0; i < num; i++)
A[i] = NULL;
HeapNode M = H->min;
HeapNode R = M->RightSibling;
HeapNode X, Z;
// 遍历 RootList
do {
X = R;
Z = X;
R = X->RightSibling;
int d = X->Degree;
// 存在 degree 相同的结点
while(A[d] != NULL) {
HeapNode Y = A[d];
// X 为⼩结点,合并在堆顶
if(X->Element > Y->Element)
NodeSwap(&X, &Y);
// 合并 X, Y
HEAP_LINK(H, X, Y);
// 合并后的度数为 d + 1
A[d] = NULL;
d++;
}
// 循环结束, A[d] = X
A[d] = X;
//PrintRoot(H);
} while(Z != M);
H->min = NULL;
// 把 A[i] 重新放⼊ RootList 中
for(int i = 0; i < num; i++) {
HeapNode X = A[i];
// A[i] 存在,插⼊ RootList
if(X != NULL)
// 插⼊空 RootList
if(H->min == NULL) {
// 只有 X, 指向⾃⼰即可
X->LeftSibling = X;
X->RightSibling = X;
// 只有X, 所以 H->min = X
H->min = X;
}
// 已有结点
else {
// 先把 X 插⼊循环链表中 (H->min 左边即可)
HEAP_INSERT_ROOT(H, X);
// 检查并更新 H->min
if(X->Element < H->min->Element)
H->min = X;
}
}
}
HeapNode HEAP_EXTRACT_MIN(FibQueue H)
{
// ⾸先把 Z 的所有⼉⼦上移到 H 的 RootList 中
HeapNode Z = H->min;
HeapNode X, Y;
if(Z != NULL) {
X = Z->FirstChild;
// 遍历 Z 的⼉⼦
while(Z->Degree--) {
// X 插⼊后右兄弟改变,提前记录
Y = X->RightSibling;
// 插⼊ RootList
HEAP_INSERT_ROOT(H, X);
// PrintRoot(H);
// 更新双亲
X->Parent = NULL;
// 下⼀个兄弟
X = Y;
}
// 将 Z 从 RootList 移除
HEAP_REMOVE_ROOT(Z);
// Z 指向⾃⼰,说明移除后堆为空
if(Z == Z->RightSibling)
H->min = NULL;
else {
// 先把 H->min 随意指向⼀个结点
H->min = Z->RightSibling;
// PrintRoot(H);
// 调整堆结构
HEAP_CONSOLIDATE(H);
}
// 移除结点,结点数 - 1
H->NodeNum--;
}
return Z;
}
// 切断 Y 和 X
void CUT(FibQueue H, HeapNode X, HeapNode Y)
{
// 把 X 从 Y 的⼉⼦列表中移除, Y.degree - 1
HEAP_REMOVE_CHILD(X, Y);
// 把 X 加⼊到 RootList 中
HEAP_INSERT_ROOT(H, X);
// X 为根
/* 判断 Y 是否要和⽗亲切断
1. Y 曾经作为根
2. Y 之后成为了孩⼦结点(因为 Z 是 Y 的⽗亲)
3. Y 的两个孩⼦被切除了 (切除第⼀个孩⼦ Mark 为 True, 切除第⼆个孩⼦ Y 和 Z 分离)
*/
void CASCADING_CUT(FibQueue H, HeapNode Y)
{
HeapNode Z = Y->Parent;
if(Z != NULL)
// 切断了⼀个⼉⼦,标记为 true, 为下次切断做准备
if(Y->Mark == false)
Y->Mark = true;
// 符合切断条件,递归向上继续切断
else {
//
CUT(H, Y, Z);
CASCADING_CUT(H, Z);
}
}
void HEAP_DECREASE_KEY(FibQueue H, HeapNode X, int k)
{
// 值只能减⼩,不能增⼤
if(k > X->Element) {
printf("new key is greater than current key\n");
return;
}
X->Element = k;
HeapNode Y = X->Parent;
// 如果 Y 存在并且 X 需要上移
if(Y != NULL && X->Element < Y->Element) {
CUT(H, X, Y);
CASCADING_CUT(H, Y);
}
// 更新 H->min
if(X->Element < H->min->Element)
H->min = X;
}
// 合并 X 和 Y 的根链表
void Concatenate(HeapNode X, HeapNode Y)
{
// 若为空,不需要合并
if(X == NULL || Y == NULL)
return;
X->RightSibling->LeftSibling = Y;
Y->RightSibling->LeftSibling = X;
X->RightSibling = Y->RightSibling;
Y->RightSibling = X->RightSibling;
}
// 合并堆 H1, H2
void HEAP_UNION(FibQueue H1, FibQueue H2)
{
// 创建⼀个新堆
FibQueue H = MAKE_HEAP();
// H 指向 H1
H->min = H1->min;
// 把 H2 的 RootList 和 H1 合并
Concatenate(H->min, H2->min);
return H;
}
// 删除堆中结点 X
void HEAP_DELETE(FibQueue H, HeapNode X)
{
HEAP_DECREASE_KEY(H, X, -INFINITY);
HEAP_EXTRACT_MIN(H);
}
Processing math: 100%。

相关文档
最新文档