二项堆

/*******************************************************
  基本思想:应用二项堆的性质:
            1.H中每个二项树遵循最小堆性质(结点的关键字最小)
            2.任意non-negative k,H中至多有以可二项树的根的度数为k(度数唯一)

  建堆时,对于单个节点,malloc结点度都是0,直接按照前后关系链接,然后根据键值进行吞并
          对于含堆情况,根据度的情况进行非递减链接
                         然后根据度的重合情况以及键值进行吞并,注意吞并过程需要修改degree

********************************************************/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<climits>
using namespace std;

typedef struct BinHeapNode BinNode;
typedef struct BinHeapNode * BinHeap;
typedef struct BinHeapNode * Position;

//结点ADT
struct BinHeapNode
{
    int key;
    int degree;
    Position parent;
    Position leftChild;
    Position sibling;
};

//用数组内的值建堆
BinHeap MakeBinHeapWithArray(int keys[], int n);

//两个堆合并
BinHeap BinHeapUnion(BinHeap &H1, BinHeap &H2);

//将H1, H2的根表合并成一个按度数的单调递增次序排列的链表
BinHeap BinHeapMerge(BinHeap &H1, BinHeap &H2);

//使H2成为H1的父节点
void BinLink(BinHeap &H1, BinHeap &H2);

//返回最小根节点的指针
BinHeap BinHeapMin(BinHeap heap);

//减少关键字的值
void BinHeapDecreaseKey(BinHeap heap, BinHeap x, int key);

//删除一个关键字
BinHeap BinHeapDelete(BinHeap &heap, int key);

//找出一个关键字
BinHeap BinHeapFind(BinHeap &heap, int key);

//打印输出堆结构
void PrintBinHeap(BinHeap heap);

//插入一个节点
BinHeap BinHeapInsert(BinHeap heap,int key);

//销毁堆
void DestroyBinHeap(BinHeap &heap);

//用数组内的值建堆
BinHeap MakeBinHeapWithArray(int keys[], int n)
{
    BinHeap heap = NULL, newHeap = NULL;
    for (int i = 0; i < n; i++)
    {

        newHeap = (BinHeap) malloc(sizeof(BinNode));
        if (newHeap == NULL)
        {
            puts("Out of the Space");
            exit(1);
        }
        memset(newHeap, 0, sizeof(BinNode));
        newHeap->key = keys[i];
        if (NULL == heap)
        {
            heap = newHeap;
        }
        else
        {
            heap = BinHeapUnion(heap, newHeap);
            newHeap = NULL;
        }

    }
    return heap;
}

//两个堆合并
BinHeap BinHeapUnion(BinHeap &H1, BinHeap &H2)
{
    Position heap = NULL, pre_x = NULL, x = NULL, next_x = NULL;
    heap = BinHeapMerge(H1, H2);
    if (heap == NULL)
    {
        return heap;
    }

    x = heap;
    next_x = x->sibling;

    while (next_x != NULL)                      //Cases 1 and 2
    {
        if ((x->degree != next_x->degree) ||
                ((next_x->sibling != NULL) && (next_x->degree == next_x->sibling->degree)))
        {
            pre_x = x;
            x = next_x;  //转127行
        }
        else if (x->key <= next_x->key)
        {
                                                /**************************************
                                                Cases 3  ,度相等,可以合并
                                                把结点键值大的放到下面去,成为键值小的的子树
                                                **************************************/
            x->sibling = next_x->sibling;
            BinLink(next_x, x);
        }
        else                             //Cases 4后面的更小
        {
            if (pre_x == NULL)
            {
                heap = next_x;
            }

            else
            {
                pre_x->sibling = next_x;//因为x要放到nextx下面去了,所以prex的右兄弟是nextx
            }

            BinLink(x, next_x);    //与109行功能相同,放较大值在下面
            x = next_x;
        }

        next_x = x->sibling;
    }
    return heap;
}                                  /*****************************************
                                    Case1表示x和nextx度不相等,不合并,则prex和nextx后移
                                    Case2表示x和nextx和nextnextx度相等,也是后移,这次不合并,但下次迭代一定合并
                                    Case3和Case4表示仅有x和nextx度相等,则发生合并
                                    Case3是前面的吞并后面的
                                    Case4是后面的吞并前面的
                                    1.度相等且pre没操作过,则heap需要改变
                                    2.度相等且pre操作过了,heap不需要改变
                                   *****************************************/


                                                              //将H1, H2的根表合并成一个按度数的单调递增次序排列的链表
BinHeap BinHeapMerge(BinHeap &H1, BinHeap &H2)
{
                                                              //heap返回堆的首地址,H3为指向新堆根结点
    BinHeap heap = NULL, firstHeap = NULL, secondHeap = NULL,
            pre_H3 = NULL, H3 = NULL;

    if (H1 != NULL && H2 != NULL)
    {
        firstHeap = H1;
        secondHeap = H2;
                                                             //整个while,firstHeap, secondHeap, pre_H3, H3都在往后顺移
        while (firstHeap != NULL && secondHeap != NULL)
        {
            if (firstHeap->degree <= secondHeap->degree)
            {
                H3 = firstHeap;                              //H3标记当前度更小的是first,并往后挪至其右兄弟
                                                            //同时度更小的树也往后挪
                firstHeap = firstHeap->sibling;
            }

            else
            {
                H3 = secondHeap;                             //H3标记当前度更小的是second,并往后挪至其右兄弟
                                                             //同时度更小的树也往后挪
                secondHeap = secondHeap->sibling;
            }

            if (pre_H3 == NULL)        //只有在首次比较度时才会用到
            {
                pre_H3 = H3;
                heap = H3;             //heap记录的是首次比较中得到的度最小的节点
            }
            else
            {
                pre_H3->sibling = H3;
                pre_H3 = H3;         /***************************************************************
                                      pre_H3是上一次比较中degree更小的结点,放在第一位(relatively)
                                      H3    是这一次比较中degree更小的结点,放在第二位(relatively)
                                      现在进行链接,第一位的右兄弟是现在的第二位
                                      并将pre_H3更新为现在得到的H3
                                     ****************************************************************/


            }
            if (firstHeap != NULL)
            {
                H3->sibling = firstHeap;
            }
            else
            {
                H3->sibling = secondHeap;
            }

        }   //ENDwhile

    }
    else if (H1 != NULL)
    {
        heap = H1;
    }
    else
    {
        heap = H2;
    }
    H1 = H2 = NULL;
    return heap;
}

//使H2成为H1的父节点
void BinLink(BinHeap &H1, BinHeap &H2)
{
    H1->parent = H2;
    H1->sibling = H2->leftChild;
    H2->leftChild = H1;
    H2->degree++;
}

//返回最小根节点的指针
BinHeap BinHeapMin(BinHeap heap)
{
    Position y = NULL, x = heap;
    int min = INT_MAX;

    while (x != NULL)
    {
        if (x->key < min)
        {
            min = x->key;
            y = x;
        }
        x = x->sibling;
    }
    return y;
}

//抽取有最小关键字的结点
BinHeap BinHeapExtractMin(BinHeap &heap)
{
    BinHeap pre_y = NULL, y = NULL, x = heap;
    int min = INT_MAX;
    while (x != NULL)
    {
        if (x->key < min)
        {
            min = x->key;
            pre_y = y;
            y = x;
        }
        x = x->sibling;
    }

    if (y == NULL)       //树为空
    {
        return NULL;
    }

    if (pre_y == NULL)   //最小值就是第一个根节点
    {
        heap = heap->sibling;
    }
    else
    {
        pre_y->sibling = y->sibling;
    }

    //将y的子结点指针reverse
    BinHeap H2 = NULL, p = NULL;
    x = y->leftChild;
    while (x != NULL)
    {
        p = x;
        x = x->sibling;
        p->sibling = H2;
        H2 = p;
        p->parent = NULL;
    }

    heap = BinHeapUnion(heap, H2);/********************************************
                                  在所有根节点访问一遍,找到最小值;
                                  将最小值的左兄弟(自定义)和右兄弟相连接(相当于删除)
                                  将此二项树剩余的子树自左向右链接
                                  将此剩余树和原树合并
                                  ********************************************/
    return y;
}

//插入一个结点
BinHeap BinHeapInsert(BinHeap heap,int key){
    BinHeap newHeap=NULL;
    newHeap=(BinHeap)malloc(sizeof(BinNode));

    if(newHeap == NULL){
        puts("Out of the Space!");
        exit(1);
    }
    memset(newHeap,0,sizeof(BinNode));
    newHeap->key=key;
    heap = BinHeapUnion(heap,newHeap);
    newHeap = NULL;
    return heap;
}

//减少关键字的值
void BinHeapDecreaseKey(BinHeap heap, BinHeap x, int key)
{
    if(key > x->key)
    {
        puts("new key is greaer than current key");
        exit(1); //不为降键
    }
    x->key = key;

    BinHeap z = NULL, y = NULL;
    y = x;
    z = x->parent;
    while(z != NULL && z->key > y->key)
    {
        swap(z->key, y->key);
        y = z;
        z = y->parent;
    }
}

//删除一个关键字
BinHeap BinHeapDelete(BinHeap &heap, int key)
{
    BinHeap x = NULL;
    x = BinHeapFind(heap, key);
    if (x != NULL)
    {
        BinHeapDecreaseKey(heap, x, INT_MIN);
        return BinHeapExtractMin(heap);
    }
    return x;
}

//找出一个关键字
BinHeap BinHeapFind(BinHeap &heap, int key)
{
    Position p = NULL, x = NULL;
    p = heap;
    while (p != NULL)
    {
        if (p->key == key)
        {
            return p;
        }
        else
        {
            if((x =BinHeapFind(p->leftChild, key)) != NULL)
            {
                return x;
            }
            p = p->sibling;
        }
    }
    return NULL;
}

//打印输出堆结构
void PrintBinHeap(BinHeap heap)
{
    if (NULL == heap)
    {
        return ;
    }
    Position p = heap;

    while (p != NULL)
    {
        printf(" (");
        printf("%d", p->key);
        //显示其孩子
        if(NULL != p->leftChild)
        {
            PrintBinHeap(p->leftChild);
        }
        printf(") ");

        p = p->sibling;
    }
}

int kp1[7] = {12,
              7, 25,
              15, 28, 33, 41
             };

int kp2[19] = {18,
               3, 37,
               6, 8, 29, 10, 44, 30, 23, 2, 48, 31, 17, 45, 32, 24, 50, 55
              };

int kp4[22] = {37, 41,
               10, 28, 13, 77,
               1, 6, 16, 12, 25, 8, 14, 29, 26, 23, 18, 11, 17, 38, 42, 27
              };

int kp5[5]={1,
            3,5,7,9,};


int main()
{
    printf("\nMutually Independent Parentheses Indicate that They are Siblings\n\nIn Each Parenthese, the first value is the root ,second is its leftchild, third one is its rightchild\n\n");
    BinHeap H1 = NULL;
    H1 = MakeBinHeapWithArray(kp1, sizeof(kp1)/sizeof(int));
    puts("第一个二叉堆H1:");
    PrintBinHeap(H1);


    BinHeap H5 = NULL;
    H5 = MakeBinHeapWithArray(kp5, sizeof(kp5)/sizeof(int));
    puts("\n\n第二个二叉堆H5:");
    PrintBinHeap(H5);

    BinHeap H3 = NULL;
    H3 = BinHeapUnion(H1, H5);
    puts("\n\n合并H1,H5后,得到H3:");
    PrintBinHeap(H3);

    BinHeap H6=NULL;
    H6=BinHeapInsert(H3,8);
    puts("\n\n插入数值8后,得到H6:");
    PrintBinHeap(H6);

    BinHeap H4 = NULL;
    H4 = MakeBinHeapWithArray(kp4, sizeof(kp4)/sizeof(int));
    puts("\n\n用于测试提取和删除的二叉堆H4:");
    PrintBinHeap(H4);

    BinHeap extractNode = BinHeapExtractMin(H4);
    if (extractNode != NULL)
    {
        printf("\n\n抽取最小的值%d后:\n", extractNode->key);
        PrintBinHeap(H4);
    }

    extractNode = BinHeapExtractMin(H4);
    if (extractNode != NULL)
    {
        printf("\n\n抽取最小的值%d后:\n", extractNode->key);
        PrintBinHeap(H4);
    }

    extractNode = BinHeapExtractMin(H4);
    if (extractNode != NULL)
    {
        printf("\n\n抽取最小的值%d后:\n", extractNode->key);
        PrintBinHeap(H4);
    }

    BinHeapDelete(H4, 12);
    puts("\n\n删除12后:");
    PrintBinHeap(H4);

    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值