二项队列的建立及其基本操作

#include <stdio.h>
#include<stdlib.h>          //二项队列支持合并,插入,删除最小值等操作,且每次操作最坏花费log N
                            //二项队列不是一个堆序,而是一群堆序的集合
#define Infinity 10000

#define MaxTrees  14            //Capacity size  2^14 - 1
#define Capacity  16383

typedef int ElementType;
typedef struct BinNode *Position;                   //二项树节点
typedef struct BinNode *BinTree;
typedef struct Collection *BinQueue;                // 二项队列

BinTree CombineTrees(BinTree T1,BinTree T2);        //合并二项树
BinQueue Merge(BinQueue H1,BinQueue H2);            //合并二项队列,二项队列即森林,二项树的集合
ElementType DeleteMin(BinQueue H);                  //删除二项队列中最小值
int IsEmpty(BinQueue H);                            //判空
int IsFull( BinQueue H );                           //判满
BinQueue Initialize(void);                          //初始化二项队列
static void DestroyTree( BinTree T );               //销毁二项树
void Destroy( BinQueue H );                         //销毁二项队列
BinQueue MakeEmpty( BinQueue H );                   //清空二项队列
ElementType FindMin( BinQueue H );                  //找到二项队列中最小值
BinQueue Insert( ElementType Item, BinQueue H );    //插入

int main(void)
{
    BinQueue H;

    H = Initialize();
    H = Insert(44,H);
    H = Insert(46,H);

    printf("%d",FindMin(H));

    printf("\nHello World!\n");

    return 0;
}

struct BinNode                              //二项树节点
{
    ElementType Element;
    Position LeftChild;
    Position NextSibling;
};

struct Collection                           //二项队列
{
    int CurrentSize;

    BinTree TheTrees[MaxTrees];             //二项队列的长度为MaxTrees
};

BinTree CombineTrees(BinTree T1,BinTree T2)  //合并T1,T2两个二项树
{
    if(T1->Element > T2->Element)
        return CombineTrees(T2,T1);         //当T1的值小于T2的值时
    T2->NextSibling = T1->LeftChild;        //T1的右兄弟指向T2的左儿子,刚开始T1的右兄弟指向无意义点
    T1->LeftChild = T2;                     //T1的左儿子指向T2
    return T1;
}

BinQueue Merge(BinQueue H1,BinQueue H2)                 //合并两个二项队列
{
    BinTree T1,T2,Carry = NULL;                         //Carry 存放上一步得来的树,可能是NULL
    int i,j;

    if(H1->CurrentSize + H2->CurrentSize >Capacity)
      printf("Merge Would Excead Capacity\n");

    H1->CurrentSize += H2->CurrentSize;                 //H1的当前大小为H1 加上 H2
    for( i = 0,j = 1; j <= H1->CurrentSize;i++,j *= 2 ) //利用j是因为当前H1的大小已经加上了H2 的大小
    {                                                   //因为二项队列中元素 按2 4 8 16 32 递增所以*2
        T1 = H1->TheTrees[i];
        T2 = H2->TheTrees[i];

        switch (!!T1 + 2 * !!T2 + 4 * !!Carry)     // !! 两个逻辑运算符,只有 1 or 0 两种结果
        {                                           //例 !!2 = 1, !!5 = 1,!!0 = 0
        case 0:                             //NO tree

        case 1:                             //only H1
            break;
        case 2:                             //only H2
            H1->TheTrees[i] = T2;
            H2->TheTrees[i] = NULL;
            break;
        case 4:                             //only Carry*
            H1->TheTrees[i] = Carry;
            Carry = NULL;
            break;
        case 3:                                     //H1 and H2
            Carry = CombineTrees(T1,T2);
            H1->TheTrees[i] = H2->TheTrees[i] = NULL;
            break;
        case 5:                                     //H1 and Carry*
            Carry = CombineTrees(T1,Carry);
            H1->TheTrees[i] = NULL;
            break;
        case 6:                                     //H2 and Carry*
            Carry = CombineTrees(T2,Carry);
            H2->TheTrees[i] = NULL;
            break;
        case 7:                                     // all trees
            H1->TheTrees[i] = Carry;
            Carry = CombineTrees(T1,T2);
            H2->TheTrees[i] = NULL;
            break;
        }
    }

 //  free(H2);

    return H1;
}

ElementType DeleteMin(BinQueue H)                   //删除最小值
{

    int i,j;
    int MinTree;
    BinQueue DeletedQueue;
    Position DeletedTree,OldRoot;
    ElementType MinItem;

    if(IsEmpty(H))
    {
        printf("Empty Binomial Queue\n");
        return -Infinity;
    }

    MinItem = Infinity;
    for( i = 0; i < MaxTrees;i++)                   //遍历整个二项队列,找到具有最小值的二项树
    {
        if(H->TheTrees[i] &&
                H->TheTrees[i]->Element < MinItem)
        {
            MinItem = H->TheTrees[i]->Element;      //最小值为 MinItem
            MinTree = i;                            //具有最小值的二项树为H->TheTrees[Mintree]
        }
    }

    DeletedTree = H->TheTrees[MinTree];         //DeletedTree and OldRoot 设为具有最小值的二项队列
    OldRoot = DeletedTree;
    DeletedTree = DeletedTree->LeftChild;           //保留最小二项树中最小节点的左孩子,右兄弟为NULL
    free(OldRoot);                                  //删除最小二项树中最小值的节点

    DeletedQueue = Initialize();                        //初始化一个二项队列DeletedQueue
                                                 //初始化该二项队列的大小为,因为删除了头节点,所以-1
    DeletedQueue->CurrentSize = (1 <<  MinTree) - 1;
    for( j = MinTree - 1; j >= 0 ; j-- )             //从该二项树前一个开始
    {                                                //将这个二项树分解为一个二项队列
        DeletedQueue->TheTrees[j] = DeletedTree;
        DeletedTree = DeletedTree->NextSibling;
        DeletedQueue->TheTrees[j]->NextSibling = NULL; //二项队列的二项树的第一个右兄弟为NULL
    }

    H->TheTrees[MinTree] = NULL;
    H->CurrentSize -= DeletedQueue->CurrentSize + 1; //减去DeletedQueue->Cur 是因为下一步合并是要加上

    Merge(H,DeletedQueue);                          //合并

    return MinItem;                                 //返回删除的最小值
}

int IsEmpty( BinQueue H )                               //判空
{
    return H->CurrentSize == 0;                         //判断当前大小是否等于0
}

int IsFull( BinQueue H )                                //判满
{                                                       //判断当前大小是否等于最大值
    return H->CurrentSize == Capacity;
}

BinQueue Initialize(void)                                   //初始化
{
    BinQueue H;
    int i;
    H = (BinQueue)malloc(sizeof(struct Collection));    //为二项队列分配空间,包括其中二项树的空间
    if(NULL == H)
        printf("Out Of Space\n");
    H->CurrentSize = 0;                                 //当前大小为0

    for( i = 0; i < MaxTrees; i++ )
        H->TheTrees[ i ] = NULL;                        //每个二项树初始指向NULL
    return H;
}

static void DestroyTree( BinTree T )                    //删除二项树
{
    if( T != NULL )
    {
        DestroyTree( T->LeftChild );                    //递归删除二项树的左儿子
        DestroyTree( T->NextSibling );                  //递归删除二项树的右兄弟
        free( T );                                      //删除该节点 or 二项树
    }
}
void Destroy( BinQueue H )                              //删除二项队列,但不删除头节点 H
{
    int i;

    for( i = 0; i < MaxTrees; i++ )
        DestroyTree( H->TheTrees[ i ] );                //从头开始一个个删除二项树
}

BinQueue MakeEmpty( BinQueue H )                        //清空二项队列
{
    int i;

    Destroy( H );                                           //
    for( i = 0; i < MaxTrees; i++ )
        H->TheTrees[ i ] = NULL;
    H->CurrentSize = 0;

    return H;
}

ElementType FindMin( BinQueue H )                              //返回最小值
{
    int i;
    ElementType MinItem;

    if( IsEmpty( H ) )
    {
       printf("Empty binomial queue");
        return 0;
    }

    MinItem = Infinity;                                         //初始化最小值
    for( i = 0; i < MaxTrees; i++ )
    {
        if( H->TheTrees[ i ] &&
                    H->TheTrees[ i ]->Element < MinItem )
            MinItem = H->TheTrees[ i ]->Element;                //从头开始一个个找最小值
    }

    return MinItem;
}

BinQueue Insert( ElementType Item, BinQueue H )                 //插入一个数
    {
        BinTree NewNode;
        BinQueue OneItem;

        NewNode =(BinTree)malloc( sizeof( struct BinNode ) );   //分配一个树节点
        if( NewNode == NULL )
            printf("Out Of Space\n");
        NewNode->LeftChild = NewNode->NextSibling = NULL;
        NewNode->Element = Item;

        OneItem = Initialize();                          //初始化一个二项队列
        OneItem->CurrentSize = 1;                        //二项队列的大小为 1,即二项队列中二项树节点数
        OneItem->TheTrees[ 0 ] = NewNode;                //该二项队列只有一个二项树 NewNode

        return Merge( H, OneItem );                      //合并 H 和该二项队列
    }

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值