第六章第四节(二项队列)

二项队列介绍:二项队列是由多个二项树组成。二项队列是一个堆序树的森林。
高度为K的二项树恰好有2^k节点。
二项队列从结构上来说是由高度不重复的若干个二项树构成的森林,高度不重复意味着相同高度的二项树在二项队列中最多存在一个。二项树的集合唯一的表示任意大小的优先队列。
例如大小为13的优先队列可以用森林B3,B2,B0表示(8+4+1)。

在这里插入图片描述

二项队列的堆序性:森林中的所有二项树都要保持相同的堆序性。

二项队列的操作
主要是合并操作,插入是合并的特殊形式,只要创建一棵单节点树并执行一次合并即可完成插入操作
二项队列的删除操作,首先找到一棵具有最小根的二项树来完成。
令该树为Bk,原始的优先队列为H,H1为去除Bk的二项树队列,去除Bk的根节点,形成二项式队列H2,再合并H1与H2,从而完成删除操作。

二项队列合并的过程:设有队列H1与H2,H1有B0,B1,B2三个二项树组成,H2有B1,B2两个二项树组成。
有一指针i=0。
1.i=0,H2没有B0,H1有B0,则直接将H1的B0作为新队列H1的B0,i++
2.i=1,H1与H2都有B1,则将B1合并,合并为B2’,由于H1中已存在B2,再将B2与B2’合并为B3’
3.i=2,H1没有B2,H2有B2,则可以将B2,直接作为H1的B2,合并完成

二项队列的数据结构
1.二项队列将是二项树的数组。
1.每个二项树的节点包括数据,第一个儿子及其右兄弟。

数据结构

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

//数据结构
#define MAXTREES 14			//子树的个数
#define CAPACITY 16383		//2^14-1
#define MIN 32768			//最小值

struct BinNode;
struct Collection;
typedef struct BinNode* Position;
typedef struct Collection* BinQueue;
typedef Position BinTree;

struct BinNode {			//二项树节点
	int Element;
	Position Left;			//左儿子
	Position NextSibling;	//右兄弟
};
struct Collection {
	int CurrentSize;				//当前二项队列中节点的个数
	BinTree TheTrees[MAXTREES];		//存储二项树的数组,每个数组为指向二项树的指针
};
//函数声明
BinQueue Initialize();				//初始化函数
BinQueue MakeEmpty(BinQueue Q);		//清空二项队列函数
BinQueue Insert(BinQueue Q,int x);	//插入函数
int DeleteMin(BinQueue Q);			//删除函数
int FindMin(BinQueue Q);			//返回最小的值
BinQueue Merge(BinQueue Q1, BinQueue Q2);	//合并两个二项队列
BinTree MergeTrees(BinTree T1,BinTree T2);	//合并两个二项树
int IsEmpty(BinQueue Q);			//判断是否为空
int IsFull(BinQueue Q);				//判断是否满
void Destroy(BinQueue Q);			//销毁二项队列
void DestroyTree(BinTree T);		//销毁某个二项树

函数实现

BinQueue Initialize() {
	//初始化函数
	BinQueue T = (BinQueue)malloc(sizeof(struct Collection));
	T->CurrentSize = 0;
	for (int i = 0; i < MAXTREES;i++) {
		T->TheTrees[i] = NULL;
	}
	return T;
}
int IsEmpty(BinQueue T) {
	//判断是否为空
	return T->CurrentSize == 0;
}
int IsFull(BinQueue T) {
	//判断是否已满
	return T->CurrentSize == CAPACITY;
}
void DestroyTree(BinTree T) {
	//递归释放节点
	if (T == NULL) {
		return;
	}
	DestroyTree(T->Left);
	DestroyTree(T->NextSibling);
	free(T);
}
BinQueue MakeEmpty(BinQueue T) {
	//把二项队列中的数据清空
	if (IsEmpty(T)) {
		return T;
	}
	for (int i = 0; i < MAXTREES; i++) {
		if (T->TheTrees[i] != NULL) {
			DestroyTree(T->TheTrees[i]);
			T->TheTrees[i] == NULL;
		}
	}
	T->CurrentSize = 0;
	return T;
}
void Destroy(BinQueue Q) {
	//释放所有空间
	if (!IsEmpty(Q)) {
		for (int i = 0; i < MAXTREES; i++) {
			if (Q->TheTrees[i] != NULL) {
				DestroyTree(Q->TheTrees[i]);
			}
		}
	}
	free(Q);
}

插入函数和findMin函数实现

BinQueue Insert(BinQueue Q, int x) {
	//插入,即有单个节点的二项队列与另一个两项队列的合并
	//创建二项树
	BinTree NewTree;
	NewTree = (BinTree)malloc(sizeof(struct BinNode));
	NewTree->Element = x;
	NewTree->Left = NULL;
	NewTree->NextSibling = NULL;
	//创建二项队列,将二项树加入队列
	BinQueue Q1 = Initialize();
	Q1->TheTrees[0] = NewTree;
	Q1->CurrentSize = 1;
	return Merge(Q, Q1);	//合并返回,完成插入
}
int FindMin(BinQueue Q) {
	//找到最小的值
	if (IsEmpty(Q)) {
		printf("二项队列为空\n");
		return -MIN;
	}
	int Min = MIN;
	for (int i = 0; i < MAXTREES; i++) {
		if (Q->TheTrees[i] != NULL && Q->TheTrees[i]->Element < Min) {
			Min = Q->TheTrees[i]->Element;		//更新最小值
		}
	}
	return Min;
}

合并函数

//合并二项树函数
BinTree MergeTrees(BinTree T1, BinTree T2) {
	//将根节点值较大的二项树合并到根节点值较小的二项树的下面
	if (T1->Element > T2->Element) {
		//保证 T1比T2的根节点小
		return MergeTrees(T2, T1);
	}
	T2->NextSibling = T1->Left;			//T2作为T1的子节点,T1的子节点作为T2的兄弟节点
	T1->Left = T2;
	return T1;
}
//合并二项队列函数
BinQueue Merge(BinQueue Q1, BinQueue Q2) {
	//两个二项队列的合并。
	BinTree T1,T2, Carry = NULL;		//carry暂存T1和T2合并的结果。
	int i, j;
	if (Q1->CurrentSize + Q2->CurrentSize > CAPACITY) {
		//合并之后,如果结点数目大于容量,合并失败。
		printf("容量不足,合并失败\n");
		return Q1;
	}
	Q1->CurrentSize += Q2->CurrentSize;
	for (i = 0, j = 1; j <= Q2->CurrentSize; i++, j *= 2) {
		//循环终止条件j<= Q2->CurrentSize,因为 2^j 必然 小于等于总节点数
		//二项队列的合并,就是二项树的合并,所有二项树合并完成,二项队列合并也就完成。
		T1 = Q1->TheTrees[i];
		T2 = Q2->TheTrees[i];
		//根据多种情况进行合并
		switch (!!T1 + 2 * !!T2 + 4 * !!Carry)
		{
		case 0:		//T1和T2和Carry都为空
		case 1:		//T1不为空
			break;
		case 2:		//T2不为空,T1为空,Q1.TheTrees[i]直接指向T2即可
			Q1->TheTrees[i] = T2;
			Q2->TheTrees[i] = NULL;
		case 4:		//只有Carry不为空,Q1->TheTrees[i]直接指向T2即可
			Q1->TheTrees[i] = Carry;
			Carry = NULL;
			break;
		case 3:		//T1和T2都不为空
			Carry = MergeTrees(T1, T2);
			Q1->TheTrees[i] = NULL;
			Q2->TheTrees[i] = NULL;
			break;
		case 5:		//Carry和T1不为空,将T1与Carry合并
			Carry = MergeTrees(T1, Carry);
			Q1->TheTrees[i] = NULL;
			break;
		case 6:		//Carry和T2不为空,将T2于Carry合并
			Carry = MergeTrees(T2, Carry);
			Q2->TheTrees[i] = NULL;
			break;
		case 7:		//T1和T2和Carry都不为空
			Q1->TheTrees[i] = Carry;
			Carry = MergeTrees(T1, T2);
			Q1->TheTrees[i] = NULL;
			break;
		}	
	}
	return Q1;
}

删除函数

int DeleteMin(BinQueue Q) {
	//弹出并删除根节点,首先找到最小值所在的二项树
	int i, j;
	int MinTree, MinItem;		//记录最小值和最小值所在二项树在二项队列中的下标
	BinQueue DeleteQueue;		//构建被删除二项树所形成的二项队列
	Position oldRoot, deleteTree;	
	if (IsEmpty(Q)) {
		printf("二项队列为空\n");
		return -MIN;
	}
	for (i = 0; i < MAXTREES; i++) {
		//遍历二项队列,找到最小的值
		if (Q->TheTrees[i]&&Q->TheTrees[i]->Element < MinItem) {
			MinItem = Q->TheTrees[i]->Element;
			MinTree = i;
		}
	}
	//删除节点
	deleteTree = Q->TheTrees[MinTree];
	oldRoot = deleteTree;
	deleteTree = oldRoot->Left;
	free(oldRoot);
	//构建被删除二项树所形成的二项队列
	DeleteQueue = Initialize();
	for (j = MinTree - 1; j >= 0;j--) {		
		//j的高度为MinTree-1,j为删除根节点的子节点,依此将j节点以后的兄弟节点加入新的二项队列
		DeleteQueue->TheTrees[j] = deleteTree;
		deleteTree = deleteTree->NextSibling;
		DeleteQueue->TheTrees[j]->NextSibling = NULL;
	}
	DeleteQueue->CurrentSize = (1 << MinTree) - 1;			//节点个数为,高度乘2-1;
	Q->TheTrees[MinTree] = NULL;
	Merge(Q,DeleteQueue);				//合并
	return MinItem;
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值