二项队列介绍:二项队列是由多个二项树组成。二项队列是一个堆序树的森林。
高度为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;
}