二項隊列的優勢:不僅支持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;
}
程序運行結果如下: