斐波那契堆

斐波那契堆
主要参考《算法导论》

1、斐波那契堆的结构

与二项堆一样,斐波那契堆是由一组最小堆的有序树构成,但堆中的树并不一定是二项树。与二项堆中树都是有序的不同,斐波那契堆中的树都是有根而无序的。

上图是一个由5棵最小堆有序树和14个结点构成的一个斐波那契堆。(A Fibonacci heap consisting of five min-heap-orderd trees and 14 nodes)

斐波那契堆的表示(实现)

typedef struct fhnode {
	int key;
	int degree; // the number of children
	bool mark;  // whether node x has lost a child
	struct fhnode *parent;
	struct fhnode *child;
	struct fhnode *left;
	struct fhnode *right;
} FHNode;

typedef struct fibheap {
	int n;              // the number of nodes currently in H
	struct fhnode *min; // point to the root of a tree containing the minimum key
} *FHeap;


2、斐波那契堆的操作



1)创建一个新的斐波那契堆

//=============================================================
// 创建一个空堆
//=============================================================
FHeap FibHeapCreate(/*FHeap H*/)
{
	FHeap H = (struct fibheap*)malloc(sizeof(struct fibheap));
	if (!H) {
		printf("H malloc error\n");
		return NULL;
	}
	H->min = NULL;
	H->n = 0;

	return H;
}

2)将结点x插入斐波那契堆中

//=============================================================
// 将x结点插到cur结点前面(使用时,保证cur存在,即cur!=NULL)
//=============================================================
static inline void InsertNode(FHNode *x, FHNode *cur)
{
	cur->left->right = x;  
	x->left = cur->left;
	x->right = cur;
	cur->left = x;	
}

//=============================================================
// 将x结点从链表中移走
//=============================================================
static inline void RemoveNode(FHNode *x)
{
	x->left->right = x->right;
	x->right->left = x->left;
}

//=============================================================
// 将含key值的结点插入堆中
//=============================================================
FHeap FibHeapInsert(FHeap H, int key)
{
	// 初始化新结点	
	FHNode *x = (FHNode *)malloc(sizeof(FHNode));
	if (!x) {
		printf("x malloc error\n");
		return NULL;
	}
	x->key = key;
	x->degree = 0;
	x->parent = NULL;
	x->child = NULL;
	x->left = x->right = x;
	x->mark = false;

	if (H->min == NULL) { // 插入第一个结点
		H->min = x;
	} else {
		InsertNode(x, H->min); // 在根链表中插入x结点		
		if (x->key < H->min->key)
			H->min = x;
	}
	H->n += 1;
}

3)寻找最小结点

//=============================================================
// 获得堆中key值最小的结点
//=============================================================
FHNode *FibHeapMinimum(FHeap H)
{
	return H->min;
}

4)合并两个斐波那契堆中

该过程只是简单的将斐波那契堆H1H2的两个根链表进行合并,然后确定一个新的最小结点。

//=============================================================
// 将堆H1与H2进行合并
//=============================================================
FHeap FibHeapUnion(FHeap H1, FHeap H2)
{
	FHeap H = FibHeapCreate();
	H->min = H1->min;

	FHNode *x = H->min;  // concatenate the root list of H2 with the root list of H
	FHNode *y = H2->min;
	if (x != NULL && y != NULL) {
		FHNode *xl = x->left;
		FHNode *yl= y->left;
		xl->right = y;
		y->left = xl;
		yl->right = x;
		x->left = yl;
	}

	// 确定H->min的位置
	if (H1->min == NULL || (H2->min != NULL && H2->min->key < H1->min->key))
		H->min = H2->min;
	H->n = H1->n + H2->n;

	return H;
}

5)抽取最小结点

该操作会调用一个consolidate过程来对根链表中的树进行合并。

过程consolidate会使用一个辅助数组A[D(H.n)],并反复执行下面的步骤:

a. 在跟链表中找出两个具有相同度数的根xy,且x.key <= y.key;

b. y链接到x:将y从跟链表中移出,并让其成为x的孩子。

直到根链表中的每个根都有不同的degree值。




//=============================================================
// 将y结点插入到x的孩子链表中
//=============================================================
static inline void FibHeapLink(FHeap H, FHNode *y, FHNode *x)
{
	RemoveNode(y);
	if (!x->child) { // x的孩子不存在
		x->child = y;
		y->left = y->right = y;
	} else {
		InsertNode(y, x->child);
	}
	x->degree += 1;
	y->parent = x;
	y->mark = false;
}

//=============================================================
// 获得堆的最大度d = lg(n)
//=============================================================
static inline int GetMaxDegree(double n)
{
	return log(n) / log(1.618);
}

//=============================================================
// 用于交换x与y
//=============================================================
static inline void SwapNode(FHNode **x, FHNode **y)
{
	FHNode *tmp = *x;
	*x = *y;
	*y = tmp;
}

//=============================================================
// 合并根链表,减少堆中树的数目
//=============================================================
void Consolidate(FHeap H)
{
	// 构建数组A,用于根结点的合并
	int md = GetMaxDegree(H->n);
	FHNode **A = (FHNode **)malloc(sizeof(FHNode *) * (md + 1));
	if (!A) {
		printf("A malloc error\n");
		return ;
	}
	for (int i = 0; i <= md; i++)
		A[i] = NULL;

 	FHNode *w = H->min;
	FHNode *last = NULL; // 用作结束的标记
	// 遍历根链表,找到度相同的两个树进行合并
	// 这里坑较多,写代码时,一定要小心
	if (w) 
		last = w->left; // 记录结束点
	while (w) {
		FHNode *next = w->right; // 临时保存,用于迭代
		FHNode *x = w;
		int d = x->degree;
		while (d <= md && A[d] != NULL) {
			FHNode *y = A[d]; 
			if (x->key > y->key) { // x与y进行交换,保证x为根
				SwapNode(&x, &y);
			}
			FibHeapLink(H, y, x); // x度数也加了1
			A[d] = NULL;	
			d += 1;
		}
		A[d] = x;	

		if (w == last) // 循环结束
			break;
		w = next;
	}

	// 根据A来重新构建堆,感觉遍历找到H->min最更省事
	H->min = NULL;
	for (int i = 0; i <= md; i++) {
		if (A[i] != NULL) {
			RemoveNode(A[i]);
			if (H->min == NULL) {			
				A[i]->left = A[i]->right = A[i];			
				H->min = A[i];
			} else {
				InsertNode(A[i], H->min);
				if (A[i]->key < H->min->key)
					H->min = A[i];
			}
		}
	}
}

//=============================================================
// 提取堆中key值最小的结点
//=============================================================
FHNode *FibHeapExtractMin(FHeap H)
{
	FHNode *z = H->min;

	if (z != NULL) {
		FHNode *cp = z->child; // 将z的孩子插入根链表中
		while (z->degree--) {	
			FHNode *next = cp->right;
			RemoveNode(cp);    // 先将cp从孩子链表中移除
			cp->parent = NULL;
			InsertNode(cp, z); // 再插入到根链表中
			cp = next;
		}
		
		// 合并根链表中的树
		if (z == z->right) {// 只有一个结点
			H->min = NULL;
		} else {
			H->min = z->right;
			RemoveNode(z); // 将z从跟链表中移除
			Consolidate(H);
		}
		H->n -= 1;
	}

	return z;
}

6)减少一个关键字

这里实现起来较为容易,但理解起来有点难(尤其是mark值)。


//=============================================================
// 将y的孩子x插入到根链表中(切除x)
//=============================================================
static inline void Cut(FHeap H, FHNode *x, FHNode *y)
{
	RemoveNode(x);
	y->degree -= 1;
	InsertNode(x, H->min);	
	x->parent = NULL;
	x->mark = false;
}

//=============================================================
// 级联切除父结点
//=============================================================
static inline void CascadingCut(FHeap H, FHNode *y)
{
	FHNode *z = y->parent;
	if (z) {
		if (y->mark == false)
			y->mark = true;
		else {
			Cut(H, y, z);
			CascadingCut(H, z);
		}
	}
}

//=============================================================
// 将x结点中的x->key值降至key(x->key < key)
//=============================================================
void FibHeapDecreaseKey(FHeap H, FHNode *x, int key)
{
	if (key > x->key) {
		printf("new key is greater than current key\n");
		return ;
	}
	x->key = key;
	FHNode *y = x->parent;
	if (y && x->key < y->key) { // 考虑x非根结点,及(key < x->parent->key)情况
		Cut(H, x, y);
		CascadingCut(H, y);
	}
	if (x->key < H->min->key)
		H->min = x;
}

7)删除一个结点

//=============================================================
// 将x结点从堆中删除
//=============================================================
void FibHeapDelete(FHeap H, FHNode *x)
{
	FibHeapDecreaseKey(H, x, MINVAL);
	FibHeapExtractMin(H);
}


下面是程序的完整代码

//===============================================================
// 斐波那契堆的实现
// 时间:2014/8/2
//===============================================================

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#define MINVAL -0xffff

typedef struct fhnode {
	int key;
	int degree; // the number of children
	bool mark;  // whether node x has lost a child
	struct fhnode *parent;
	struct fhnode *child;
	struct fhnode *left;
	struct fhnode *right;
} FHNode;

typedef struct fibheap {
	int n;              // the number of nodes currently in H
	struct fhnode *min; // point to the root of a tree containing the minimum key
} *FHeap;

//=============================================================
// 创建一个空堆
//=============================================================
FHeap FibHeapCreate(/*FHeap H*/)
{
	FHeap H = (struct fibheap*)malloc(sizeof(struct fibheap));
	if (!H) {
		printf("H malloc error\n");
		return NULL;
	}
	H->min = NULL;
	H->n = 0;

	return H;
}

//=============================================================
// 将x结点插到cur结点前面(使用时,保证cur存在,即cur!=NULL)
//=============================================================
static inline void InsertNode(FHNode *x, FHNode *cur)
{
	cur->left->right = x;  
	x->left = cur->left;
	x->right = cur;
	cur->left = x;	
}

//=============================================================
// 将x结点从链表中移走
//=============================================================
static inline void RemoveNode(FHNode *x)
{
	x->left->right = x->right;
	x->right->left = x->left;
}

//=============================================================
// 将含key值的结点插入堆中
//=============================================================
FHeap FibHeapInsert(FHeap H, int key)
{
	// 初始化新结点	
	FHNode *x = (FHNode *)malloc(sizeof(FHNode));
	if (!x) {
		printf("x malloc error\n");
		return NULL;
	}
	x->key = key;
	x->degree = 0;
	x->parent = NULL;
	x->child = NULL;
	x->left = x->right = x;
	x->mark = false;

	if (H->min == NULL) { // 插入第一个结点
		H->min = x;
	} else {
		InsertNode(x, H->min); // 在根链表中插入x结点		
		if (x->key < H->min->key)
			H->min = x;
	}
	H->n += 1;
}

//=============================================================
// 获得堆中key值最小的结点
//=============================================================
FHNode *FibHeapMinimum(FHeap H)
{
	return H->min;
}
	
//=============================================================
// 将堆H1与H2进行合并
//=============================================================
FHeap FibHeapUnion(FHeap H1, FHeap H2)
{
	FHeap H = FibHeapCreate();
	H->min = H1->min;

// 	FHNode *x = H->min;
// 	FHNode *y = H2->min;
// 	FHNode *yl = y->left;
// 	x->left->right = y;
// 	y->left = x->left;
// 	x->left = yl;
// 	yl->right = x;
	FHNode *x = H->min;  // concatenate the root list of H2 with the root list of H
	FHNode *y = H2->min;
	if (x != NULL && y != NULL) {
		FHNode *xl = x->left;
		FHNode *yl= y->left;
		xl->right = y;
		y->left = xl;
		yl->right = x;
		x->left = yl;
	}

	// 确定H->min的位置
	if (H1->min == NULL || (H2->min != NULL && H2->min->key < H1->min->key))
		H->min = H2->min;
	H->n = H1->n + H2->n;

	return H;
}

//=============================================================
// 将y结点插入到x的孩子链表中
//=============================================================
static inline void FibHeapLink(FHeap H, FHNode *y, FHNode *x)
{
	RemoveNode(y);
	if (!x->child) { // x的孩子不存在
		x->child = y;
		y->left = y->right = y;
	} else {
		InsertNode(y, x->child);
	}
	x->degree += 1;
	y->parent = x;
	y->mark = false;
}

//=============================================================
// 获得堆的最大度d = lg(n)
//=============================================================
static inline int GetMaxDegree(double n)
{
	return log(n) / log(1.618);
}

//=============================================================
// 用于交换x与y
//=============================================================
static inline void SwapNode(FHNode **x, FHNode **y)
{
	FHNode *tmp = *x;
	*x = *y;
	*y = tmp;
}

//=============================================================
// 合并根链表,减少堆中树的数目
//=============================================================
void Consolidate(FHeap H)
{
	// 构建数组A,用于根结点的合并
	int md = GetMaxDegree(H->n);
	FHNode **A = (FHNode **)malloc(sizeof(FHNode *) * (md + 1));
	if (!A) {
		printf("A malloc error\n");
		return ;
	}
	for (int i = 0; i <= md; i++)
		A[i] = NULL;

 	FHNode *w = H->min;
	FHNode *last = NULL; // 用作结束的标记
	// 遍历根链表,找到度相同的两个树进行合并
	// 这里坑较多,写代码时,一定要小心
	if (w) 
		last = w->left; // 记录结束点
	while (w) {
		FHNode *next = w->right; // 临时保存,用于迭代
		FHNode *x = w;
		int d = x->degree;
		while (d <= md && A[d] != NULL) {
			FHNode *y = A[d]; 
			if (x->key > y->key) { // x与y进行交换,保证x为根
				SwapNode(&x, &y);
			}
			FibHeapLink(H, y, x); // x度数也加了1
			A[d] = NULL;	
			d += 1;
		}
		A[d] = x;	

		if (w == last) // 循环结束
			break;
		w = next;
	}

	// 根据A来重新构建堆,感觉遍历找到H->min最更省事
	H->min = NULL;
	for (int i = 0; i <= md; i++) {
		if (A[i] != NULL) {
			RemoveNode(A[i]);
			if (H->min == NULL) {			
				A[i]->left = A[i]->right = A[i];			
				H->min = A[i];
			} else {
				InsertNode(A[i], H->min);
				if (A[i]->key < H->min->key)
					H->min = A[i];
			}
		}
	}
}

//=============================================================
// 提取堆中key值最小的结点
//=============================================================
FHNode *FibHeapExtractMin(FHeap H)
{
	FHNode *z = H->min;

	if (z != NULL) {
		FHNode *cp = z->child; // 将z的孩子插入根链表中
		while (z->degree--) {	
			FHNode *next = cp->right;
			RemoveNode(cp);    // 先将cp从孩子链表中移除
			cp->parent = NULL;
			InsertNode(cp, z); // 再插入到根链表中
			cp = next;
		}
		
		// 合并根链表中的树
		if (z == z->right) {// 只有一个结点
			H->min = NULL;
		} else {
			H->min = z->right;
			RemoveNode(z); // 将z从跟链表中移除
			Consolidate(H);
		}
		H->n -= 1;
	}

	return z;
}

//=============================================================
// 将y的孩子x插入到根链表中(切除x)
//=============================================================
static inline void Cut(FHeap H, FHNode *x, FHNode *y)
{
	RemoveNode(x);
	y->degree -= 1;
	InsertNode(x, H->min);	
	x->parent = NULL;
	x->mark = false;
}

//=============================================================
// 级联切除父结点
//=============================================================
static inline void CascadingCut(FHeap H, FHNode *y)
{
	FHNode *z = y->parent;
	if (z) {
		if (y->mark == false)
			y->mark = true;
		else {
			Cut(H, y, z);
			CascadingCut(H, z);
		}
	}
}

//=============================================================
// 将x结点中的x->key值降至key(x->key < key)
//=============================================================
void FibHeapDecreaseKey(FHeap H, FHNode *x, int key)
{
	if (key > x->key) {
		printf("new key is greater than current key\n");
		return ;
	}
	x->key = key;
	FHNode *y = x->parent;
	if (y && x->key < y->key) { // 考虑x非根结点,及(key < x->parent->key)情况
		Cut(H, x, y);
		CascadingCut(H, y);
	}
	if (x->key < H->min->key)
		H->min = x;
}

//=============================================================
// 将x结点从堆中删除
//=============================================================
void FibHeapDelete(FHeap H, FHNode *x)
{
	FibHeapDecreaseKey(H, x, MINVAL);
	FibHeapExtractMin(H);
}

//=============================================================
// 在堆中查找包含key的结点
//=============================================================
FHNode *FibHeapSearch(FHNode *x, int key)
{
	FHNode *pos = x;
	FHNode *y = NULL;

	if (pos) {
		do {
			if (pos->key == key)
				return pos;
			else if (pos->child)	
				y = FibHeapSearch(pos->child, key);
			pos = pos->right;	
		} while (pos != x);
	}

	return y;
}

//=============================================================
// 打印堆中所有结点的key值
//=============================================================
void FibHeapPrint(FHNode *x)
{
	FHNode *pos = x;

	if (pos) {
		do {
			printf("%d\t", pos->key);
			if (pos->child) {		
				FibHeapPrint(pos->child);
			}
			pos = pos->right;	
		} while (pos != x);
	}
}


//=============================================================
// 从pos处开始打印循环双链表中的key值(调试时使用)
//=============================================================
void PrintDList(FHNode *pos)
{
	FHNode *cur = pos;

	if (cur) {
		printf("%d\t", cur->key);
		cur = cur->right;
	}
	while (cur != pos) {
		printf("%d\t", cur->key);
		cur = cur->right;
	}

	// 	if (cur) {
	// 		do {
	// 			printf("%d\t", cur->key);
	// 			cur = cur->right;
	// 		} while (cur != pos);
	// 	}
	// 
	// 	if (cur) {
	// 		while (cur->right != pos) {
	// 			printf("%d\t", cur->key);
	// 			cur = cur->right;
	// 		}
	// 		if (cur) { // 打印最后一个结点
	// 			printf("%d\t", cur->key);
	// 		}
	// 	}

	printf("\n");
}

int main()
{
	int arr[] = {45, 65, 23, 9, 6, 21, 97, 60, 23, 7, 90, 61, 123, 987, 51, 11}; // 9
	int len = sizeof(arr) / sizeof(arr[0]);

	FHeap H1 = FibHeapCreate();
	for (int i = 0; i < len - 5; i++) {
		printf("H1 insert:%d\n", arr[i]);
		FibHeapInsert(H1, arr[i]);
	}
	printf("\n");

	FHeap H2 = FibHeapCreate();
	for (int i = len - 5; i < len; i++) {
		printf("H2 insert:%d\n", arr[i]);
		FibHeapInsert(H2, arr[i]);
	}
	printf("\n");

	printf("Union:");
	FHeap H = FibHeapUnion(H1, H2);

	printf("PrintKey:\n");
	FibHeapPrint(H->min);
 	printf("\n");

	printf("ExtractMinKey:\n");
	/*for (int i = 0; i < len; i++)*/ {
		FHNode *min = FibHeapExtractMin(H);
		printf("%d\n", min->key);
	}

	printf("PrintKey:\n");
	FibHeapPrint(H->min);
	printf("\n");

	printf("Search:\n");
	FHNode *x = FibHeapSearch(H->min, 23);
	printf("%d\n", x->key);
	x = FibHeapSearch(H->min, 60);
	printf("%d\n", x->key);

	printf("DecreaseKey: %d-->3\n", x->key);
	FibHeapDecreaseKey(H, x, 3);
	printf("MinimumKey: ");
	FHNode *min = FibHeapMinimum(H);
	printf("%d\n", min->key);
	
	printf("DeleteKey: %d\n", min->key);
	FibHeapDelete(H, min);
	printf("ExtractMinKey: ");
	min = FibHeapExtractMin(H);
	printf("%d\n", min->key);

	system("pause");
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值