一种二项队列实现
程序员文章站
2022-03-12 23:03:55
...
二项队列的优势:不仅支持insert、delete_min和merger操作,而且最坏情况下运行时间为O(logN),插入操作平均花费 时间为O(1)
二项队列不是一颗堆序的树,而是堆序树的集合,其中每一颗堆序树又称为二项树
二项队列性质:高度为K的二项树恰好有2的K次幂个节点
我们可以使用二项树的集合唯一地表示任意大小的优先队列
#include<stdio.h>
#include<stdlib.h>
struct BinNode {
int data;
struct BinNode *firstchild;
struct BinNode *nextsibling;
};
struct BinQueue{
int currentsize;//当前节点数目
struct BinNode *trees[4];
};
void print_BinQueue(struct BinQueue *H);
//初始化一个二项队列
struct BinQueue *init_BinQueue(void)
{
struct BinQueue *H;
int i;
H = malloc(sizeof(struct BinQueue));
if(NULL == H)
{
printf("Error: out of memory!!\n");
return NULL;
}
H->currentsize = 0;
return H;
}
//合并两个等高度二项队列
struct BinNode *merger_equal_trees(struct BinNode *H1,struct BinNode *H2)
{
//找出哪个二项队列的根较小
if(H1->data > H2->data)
return merger_equal_trees(H2,H1);
//将较小根值的二项队列的孩子指针,赋值给根值较大二项队列的兄弟指针
H2->nextsibling = H1->firstchild;
//将较大根值的二项队列,赋值给根值较小二项队列的孩子指针
H1->firstchild = H2;
return H1;
}
struct BinQueue *merger(struct BinQueue *H1,struct BinQueue *H2)
{
struct BinNode *T1,*T2,*carry=NULL;
int i,j;
//首先判断是否会发生溢出
if(H1->currentsize + H2->currentsize > 15)
{
printf("Error: out of capacity!!\n");
return H1;
}
//更新H1的节点数
H1->currentsize += H2->currentsize;
//分别合并,先从低位开始
for(i=0;i<4;i++)
{
T1 = H1->trees[i];
T2 = H2->trees[i];
//carry T2 T1所有0-7组合
switch(!!T1 + 2*!!T2 + 4*!!carry)
{
case 0:
case 1:
break;
case 2:
H1->trees[i] = H2->trees[i];
H2->trees[i] = NULL;
break;
case 3:
carry = merger_equal_trees(T1,T2);
H1->trees[i] = NULL;
H2->trees[i] = NULL;
break;
case 4:
H1->trees[i] = carry;
carry = NULL;
break;
case 5:
carry = merger_equal_trees(T1,carry);
H1->trees[i] = NULL;
break;
case 6:
carry = merger_equal_trees(T2,carry);
H2->trees[i] = NULL;
break;
case 7:
H1->trees[i] = carry;
carry = merger_equal_trees(T1,T2);
H2->trees[i] = NULL;
break;
}
}
return H1;
}
struct BinQueue *insert_BinQueue(int data,struct BinQueue *H)
{
//首先创建一个只有B0的二项队列
struct BinQueue *H1;
int i;
H1 = malloc(sizeof(struct BinQueue));
if(NULL == H1)
{
printf("Error: out of memory!!\n");
return NULL;
}
H1->trees[0] = malloc(sizeof(struct BinNode));
if(NULL == H1->trees[0])
{
printf("Error: out of memory!!\n");
return NULL;
}
H1->trees[0]->data = data;
H1->trees[0]->firstchild = H1->trees[0]->nextsibling = NULL;
H1->currentsize = 1;
//将刚创建的二项队列和原有的二项队列合并
H = merger(H,H1);
return H;
}
int delete_min_BinQueue(struct BinQueue *H)
{
int min_value = 255;//确保大于任何一个节点中数据
int i,j;
int min_i;
struct BinNode *min_T,*min_BinTree;
struct BinQueue *Delete_Queue = NULL;
//首先判断该二项队列中节点个数
if(0 == H->currentsize)
{
printf("Error: the current BinQueue is already empty!!\n");
return -1;
}
//找到最小值所在的二项树
for(i=0;i<4;i++)
{
if(NULL != H->trees[i] && H->trees[i]->data < min_value)
{
min_value = H->trees[i]->data;
min_i = i;
min_T = H->trees[i];
}
}
//将找到的二项树从原二项队列中清除
H->trees[min_i] = NULL;
//创建一个新二项队列
Delete_Queue = init_BinQueue();
if(NULL == Delete_Queue)
{
printf("Error: create delete BinQueue failed\n");
return -1;
}
//保存找到的二项树指针
min_BinTree = min_T;
//先处理最大子二项树
Delete_Queue->trees[min_i-1] = min_T->firstchild;
min_T = min_T->firstchild->nextsibling;
//释放找到二项树的root节点
free(min_BinTree);
//将找到的二项树分解成若干个子二项树,来填充新二项队列
for(j=min_i-2;j>=0;j--)
{
Delete_Queue->trees[j] = min_T;
min_T = min_T->nextsibling;
//将前面二项树root节点的兄弟指针置空,使他们各自独立成二项树
Delete_Queue->trees[j+1]->nextsibling = NULL;
}
//将Delete BinQueue与H进行合并
H = merger(H,Delete_Queue);
return min_value;
}
void print_preorder(struct BinNode *T)
{
if(NULL == T)
return;
printf("%d ",T->data);
print_preorder(T->firstchild);
print_preorder(T->nextsibling);
}
void print_BinQueue(struct BinQueue *H)
{
int i;
if(NULL == H)
return;
for(i=0;i<4;i++)
{
printf("B[%d]的前序遍历为:",i);
print_preorder(H->trees[i]);
printf("\n");
}
}
int main(void)
{
struct BinQueue *g_H;
int i;
g_H = init_BinQueue();
for(i=1;i<=15;i++)
{
g_H = insert_BinQueue(i,g_H);
}
print_BinQueue(g_H);
printf("-----------start delete_min()----------\n");
delete_min_BinQueue(g_H);
printf("-----------after delete_min()----------\n");
print_BinQueue(g_H);
return 0;
}
程序运行结果如下:
上一篇: HTML如何制作表单
下一篇: php rmdir()怎么删除非空目录