/*******************************************************
基本思想:应用二项堆的性质:
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;
}
二项堆
最新推荐文章于 2022-02-18 14:22:28 发布
638

被折叠的 条评论
为什么被折叠?



