1 // 二项堆.cpp : 定义控制台应用程序的入口点。
2 //
3 /***********************************************************
4
5 二项堆,算法数据结构复习
6
7 Designer By cslave
8
9
10
11 **************************************************************/
12
13
14 #include "stdafx.h"
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <iostream>
18 using namespace std;
19 #define MaxTrees 14
20 #define INF (30000L)
21 #define Capacity (16383)
22 #define FatalError(Str) fprintf(stderr,"%s\n",Str),exit(1)
23 typedef int ElementType ;
24 typedef struct BinNode* Position;
25 typedef struct BinNode* BinTree;
26 struct BinNode //二项堆树节点
27 {
28 ElementType Element;
29 Position LeftChild;
30 Position NextSibling;
31 };
32 struct Collection //二项堆
33 {
34 int CurrentSize;
35 BinTree TheTrees[MaxTrees];
36 };
37
38 typedef struct Collection* BinQueue; //二项堆类型的指针
39
40 bool IsEmpy(BinQueue H) //二项堆是否为空 即是否含有节点
41 {
42 return H->CurrentSize==0;
43 }
44
45 bool IsFul(BinQueue H) //二项堆是否为满,即节点数是否满载
46 {
47 return H->CurrentSize==Capacity;
48 }
49
50 BinQueue Initialize() //二项堆初始化
51 {
52 BinQueue H;
53 int i ;
54 H=(BinQueue)malloc(sizeof (struct Collection));
55 if(H==NULL)
56 FatalError("out of space");
57 H->CurrentSize=0;
58 for(i=0;i<MaxTrees;i++)
59 H->TheTrees[i]=NULL; //初始化每一个二项堆树节点为空
60 return H;
61 }
62
63 void DestroyTree(BinTree T) //二项堆树节点的析构
64 {
65 if(T!=NULL)
66 {
67 DestroyTree(T->LeftChild);
68 DestroyTree(T->NextSibling);
69 free(T);
70 }
71 }
72
73 void Destroy(BinQueue H) //二项堆的析构
74 {
75 int i;
76 for(i=0;i<MaxTrees;i++)
77 DestroyTree(H->TheTrees[i]);
78 }
79
80
81 BinQueue MakeEmpty(BinQueue H) //二项堆的置空
82 {
83 int i;
84 Destroy(H);
85 for(i=0;i<MaxTrees;i++)
86 H->TheTrees[i]=NULL;
87 H->CurrentSize=0;
88 return H;
89 }
90
91 BinTree CombineTrees(BinTree T1,BinTree T2) //子二项树的合并
92 {
93 if(T1->Element>T2->Element)
94 return CombineTrees(T2,T1); //调整节点,根较小的二项树作为主合并点
95 T2->NextSibling=T1->LeftChild; //二项堆为了快速找出根的子树,采用的是左孩子,右兄弟存储策略 将小根的左孩子作为大根的右兄弟
96 T1->LeftChild=T2; //将大根作为小根的左孩子
97 return T1;
98 }
99
100 BinQueue Merge(BinQueue H1,BinQueue H2) //子二项树的合并
101 {
102 BinTree T1,T2,Carry=NULL;
103 int i,j;
104 if(H1->CurrentSize+H2->CurrentSize>Capacity) //边界判定
105 FatalError("Node Num too large!");
106 H1->CurrentSize+=H2->CurrentSize;
107 for(i=0,j=1;j<=H1->CurrentSize;i++,j*=2) //j作为判定条件,判断二项堆可以合并为几阶
108 {
109 T1=H1->TheTrees[i]; //抽取对等子二项树树根
110 T2=H2->TheTrees[i];
111 switch(!!T1+2*!!T2+4*!!Carry) //此处用法很巧妙 1 !!取一个数的布尔真值 2 我们用三个变量的二进制三位码表征八种情况
112 {
113 case 0: //没有子二项树
114 case 1:break; //只有T1 因为我们返回H1 所以什么都不用做。
115 case 2: //只有T2,我们仅需将T2赋值给H1即可
116 H1->TheTrees[i]=T2;
117 H2->TheTrees[i]=NULL;
118 break;
119 case 4: //上一阶二项树有合并到本阶 ,本阶无二项树
120 H1->TheTrees[i]=Carry;
121 Carry=NULL;
122 break;
123 case 3: //本阶两堆都有要合并的子二项树,则产生高阶进位
124 Carry=CombineTrees(T1,T2);
125 H1->TheTrees[i]=H2->TheTrees[i]=NULL;
126 break;
127 case 5: //有低阶来的进位,同时又自身的T1 则合并进位和T1
128 Carry=CombineTrees(T1,Carry);
129 H1->TheTrees[i]=NULL;
130 break;
131 case 6: // 有低阶来的进位,同时又自身的T2 则合并进位和T2
132 Carry=CombineTrees(T2,Carry);
133 H2->TheTrees[i]=NULL;
134 break;
135 case 7: //此处尚有迷惑,我个人感觉因该是先Tmp=Carry; Carry=CombineTrees(T1,T2);H1->TheTrees[i]=Tmp
136 H1->TheTrees[i]=Carry;
137 Carry=CombineTrees(T1,T2);
138 H2->TheTrees[i]=NULL;
139 break;
140 }
141
142 }
143 return H1;
144 }
145
146
147 BinQueue insert(BinQueue H,ElementType X) //节点的插入,在二项堆插入节点值X
148 {
149 BinQueue TmpQue;
150 BinTree NewNode;
151 NewNode=(BinTree)malloc(sizeof(struct BinNode));
152 if(NewNode==NULL)
153 FatalError("memory insufficient!");
154 NewNode->Element=X;
155 NewNode->LeftChild=NULL;
156 NewNode->NextSibling=NULL;
157 TmpQue=Initialize();
158 TmpQue->CurrentSize=1;
159 TmpQue->TheTrees[0]=NewNode; //创建仅含一个树节点的二项堆再与原来的堆做合并操作
160 return Merge(H,TmpQue);
161 }
162
163 ElementType DeleteMin(BinQueue H) //删除二项堆最小元素
164 {
165 int i,j;
166 int MinTree;
167 BinQueue DeleteQueue;
168 Position DeleteTree,OldRoot;
169 ElementType MinItem;
170 if(IsEmpy(H))
171 {
172 FatalError("BinQueue is Empty!");
173 return -INF;
174 }
175 MinItem= INF;
176 for(i=0;i<MaxTrees;i++) //由二项堆性质可知,二项堆最小元素必然在每一个二项树的树根
177 {
178 if(H->TheTrees[i]&&H->TheTrees[i]->Element<MinItem) //找到并保存二项堆的最小值和索引i
179 {
180 MinItem=H->TheTrees[i]->Element;
181 MinTree=i;
182 }
183 }
184 DeleteTree=H->TheTrees[MinTree];
185 OldRoot=DeleteTree;
186 DeleteTree=DeleteTree->LeftChild; //指向左儿子
187 free(OldRoot);
188 DeleteQueue=Initialize(); //初始化一二项堆
189 DeleteQueue->CurrentSize=(1<<MinTree)-1; //更新堆元素个数
190 for(j=MinTree-1;j>=0;j--)
191 {
192 DeleteQueue->TheTrees[j]=DeleteTree;//将左儿子作为堆的小一阶二项树
193 DeleteTree=DeleteTree->NextSibling; //将左儿子的右兄弟作为下一个处理对象
194 DeleteQueue->TheTrees[j]->NextSibling=NULL; //处理后将二项树根节点右兄弟置空
195 }
196 H->TheTrees[MinTree]=NULL;//已经处理完最小树,将该树在堆数组中的元素置空
197 H->CurrentSize-=DeleteQueue->CurrentSize+1;//从原始堆中减去处理掉的二项树元素个数,加1是因为还有二项树根我们删除了
198 Merge(H,DeleteQueue);//合并H和新二项堆。
199 return MinItem;
200
201
202
203 }
204
205
206
207
208
209
210
211
212
213
214
215 int _tmain(int argc, _TCHAR* argv[])
216 {
217 ElementType arr[8]={12,24,21,65,14,16,18,26};
218 BinQueue H=Initialize();
219 for(int i=0;i<8;i++)
220 insert(H, arr[i]);
221 for(int j=0;j<8;j++)
222 cout<<DeleteMin(H)<<" ";
223
224 return 0;
225 }