问题描述
认识二项树、二项堆数据结构,并能应用该结构解决实际问题。
二项堆是二项树的集合,二项树是一种递归定义的有序树。它的递归定义如下:
(1) 二项树B0只有一个结点;
(2) 二项树Bk由两棵二项树B(k-1)组成的,其中一棵树是另一棵树根的最左孩子。
如下图所示:
图(a) 递归定义的二项树Bk,三角形表示有根子树。
图(b) 二项树B0 到B4 。
图(c)另一种角度看二项树Bk
二项树具有如下的性质:
1. 对于树Bk,该树含有2k个节点;
2. 树的高度是k;
3. 在深度为i中含有 节点,其中i = 0, 1,2 ... , k;
二项堆H是具备如下性质的二项树的集合:
1. H中的每个二项树遵循最小堆性质;
2. 对于任意的整数k的话,H中最多有一个二项树的根的度数是k;
基本要求
(1) 设计二项堆ADT,其上的基本操作包括:Make Heap (x), Find-Min, Union,
Insert,Extract-Min,Decrease Key (x),Delete。
(2)实现二项堆ADT,包括实现二项堆的存储结构以及其上的基本操作,并分析基本操作的时间复杂性。
(3)实现二项堆ADT的基本操作演示(要求应用图形界面)。
问题分析
认识二项树
二项树是一种递归定义的有序树。它的递归定义如下:
(1)二项树B0只有一个结点;
(2)二项树Bk由两棵二项树B(k-1)组成的,其中一棵树是另一棵树根的最左孩子。
认识二项堆
二项堆H是具备如下性质的二项树的集合:
(1)H中的每个二项树遵循最小堆性质,父节点的关键字小于等于其孩子节点的关键字
(2)对于任意的非负整数k,H中最多有一个二项树的根的度数是k,即二项堆中不能有度数相同的二项树。
(3)兄弟关系:节点38和22,21和39。父子关系:节点22和46。
样例设计与算法设计思路
样例设计
输入:每一个堆要插入的关键字(也可随机生成)。
输出:根据选择的操作,输出对应堆由哪几个二项树组成,并且每一个二项树的根、每个节点的孩子节点、兄弟节点、父亲节点。每个节点后的括号内是对应度数。
算法设计思路
构造二项堆节点结构体
成员包括关键字key,度数degree,左孩子leftchild,父节点parent,兄弟节点next和构造函数。构造节点要对关键字进行赋值,将度数初始化为0,左孩子、父节点、兄弟节点初始化为空指针。
struct BinomialNode {
int key;//关键字
int degree;//度数
BinomialNode* leftchild;//左孩子
BinomialNode* parent;//父节点
BinomialNode* next;//兄弟节点
BinomialNode(int k) :key(k), degree(0), leftchild(NULL), parent(NULL), next(NULL) {}
};
构造二项堆类
私有成员为根节点。成员函数有构造节点函数,时间复杂度O(1)。。
BinomialNode* MakeNode(int Key) {//构造
BinomialNode* node;
node = new BinomialNode(Key);
return node;
}
查找函数
利用递归查找,从根节点开始,当与要查找关键字相同时返回,若不是低轨道子树里找,从子树返回没找到,则再向下一个兄弟节点找下一个子树。
BinomialNode* search(BinomialNode* heap, int key) {//查找是否存在
BinomialNode* leftchild;
BinomialNode* parent = heap;
while (parent != NULL) {
if (parent->key == key) {
return parent;
}
else {
leftchild = search(parent->leftchild, key);//在子树里找
if (leftchild != NULL) {//找到
return leftchild;
}
parent = parent->next;//下一个子树
}
}
return NULL;
}
找最小节点值函数
根据二项树的性质可知,最小值一定在跟链表中,所以找出跟链表中的最小值即可。找最小节点同理。时间复杂度为O(logn)
int FindMin() {//找最小节点值
BinomialNode* fin = mRoot->next;
BinomialNode* minnode = mRoot;
while (fin != NULL) {
if (fin->key < minnode->key)minnode = fin;
fin = fin->next;
}
return minnode->key;
}
BinomialNode* findmin() {//找最小节点
if (mRoot == NULL)return NULL;
BinomialNode* fin = mRoot->next;
BinomialNode* minnode = mRoot;
while (fin != NULL) {
if (fin->key < minnode->key)minnode = fin;
fin = fin->next;
}
return minnode;
}
合并操作函数
(1)首先将要合并的两个二项堆按照堆中二项树的度数从小到大连接成一个链表,
(2)然后需要三个指针当前节点x,前一个节点pre_x,下一个节点next_x具体分为以下几种情况:
1.如果当前节点和下一个节点的度数不等时,接着向下一个兄弟节点遍历。
2.如果当前节点、下一个节点和再下一个节点的度数都相等时,人继续向兄弟节点遍历,因为要找最后两个度数不相同的节点。
3.如果当前节点和下一个节点度数相同,与再下一个节点度数不同,或者此时在下一个节点不存在。需要将关键字较大的节点作为较小节点的子节点,合成一个二项树。将新链表中根节点度数相同的二项树合成一个树,直到所有根节点度数都不同。
时间复杂度为O(logn)
BinomialNode* unionroot(BinomialNode* h1, BinomialNode* h2) {//合并两个根节点链表,按度数递增合并
if (h1 == NULL) return h2;
else if (h2 == NULL) return h1;
BinomialNode* node = NULL;
if (h1->degree < h2->degree) {//找度数较小的
node = h1;
node->next = unionroot(h1->next, h2);
}
else {
node = h2;
node->next = unionroot(h2->next, h1);
}
return node;
}
void link(BinomialNode* leftchild, BinomialNode* heap) {//两个相邻的度数相同的二项树树合并成新二项树
leftchild->parent = heap;
leftchild->next = heap->leftchild;
heap->leftchild = leftchild;
heap->degree++;
}
BinomialNode* BinomialHeap::Union(BinomialNode* h1, BinomialNode* h2) {//合并
BinomialNode* heap;
BinomialNode* pre_x, * x, * next_x;
heap = unionroot(h1, h2);//h1,h2合并成一个按度数递增的链表
if (heap == NULL)return NULL;
pre_x = NULL;
x = heap;
next_x = x->next;
while (next_x != NULL) {//遍历所有二项树的根
if ((x->degree != next_x->degree) || ((next_x->next != NULL) && (next_x->degree == next_x->next->degree))) {//和下一节点度数不同。或者和下两个度数相等的时候(要找到最后度数相同的)
pre_x = x;//不操作,直接向下一节点
x = next_x;
}
else if (x->key <= next_x->key) {//本节点的关键字小
x->next = next_x->next;
link(next_x, x);//x为next_x的父节点
}
else {//本届点关键字大
if (pre_x == NULL) {//如果此节点为第一个根节点
heap = next_x;
}
else {
pre_x->next = next_x;
}
link(x, next_x);//x作为next_x的子节点
x = next_x;
}
next_x = x->next;
}
return heap;
}
插入函数
即将要插入的关键字构造成一个节点,然后将此节点作为一个二项堆与原来的二项堆合并操作。时间复杂度与合并相同为O(logn)。
void BinomialHeap::Insert(int key) {//插入
if (search(mRoot, key) != NULL) {
cout << "无法插入关键字" << key << "已存在" << endl;
return;
}
BinomialNode* node1;
node1 = MakeNode(key);
if (node1 == NULL)return;
mRoot = Union(mRoot, node1);
}
移除最小节点函数
(1)先找到最小节点的位置,然后将最小的根节点所在的二项树从二项堆中移除,
(2)剩下的连成一个二项堆。
(3)将最小节点所在二项树的根节点删除,
(4)将剩下的子树组成的链表变成新的二项堆(即将原来的兄弟关系反过来,next指向度数更大的)。
(5)将新的二项堆和移除最小节点所在二项树的堆合并。
时间复杂度为O(logn)
BinomialNode* transform(BinomialNode* heap) {//将子树转换成二项堆,反转二项堆heap
BinomialNode* next;
BinomialNode* pre = NULL;//原来的前一个节点
if (heap==NULL)return heap;
heap->parent = NULL;
while (heap->next!=NULL) {
next = heap->next;
heap->next = pre;
pre = heap;
heap = next;
heap->parent = NULL;
}
heap->next = pre;
return heap;
}
void BinomialHeap::Extract_Min() {//移除最小值
if (mRoot == NULL) return;
BinomialNode* root = mRoot;
BinomialNode* minnode = findmin();//找到最小值所在的节点
BinomialNode* pre, * pos;
pre = NULL;
pos = root;
while (pos != minnode) {//pre为最小节点前的节点
pre = pos;
pos = pos->next;
}
if (pre) {//最小节点不是第一个根节点
pre->next = minnode->next;
}
else {//最小节点是第一个根节点
root = minnode->next;
}
root = Union(root, transform(minnode->leftchild));//将删掉最小节点的堆和原堆合并
delete minnode;
mRoot = root;
}
减小节点的值函数
先确定检校后的值是否存在,如果不存在要保持该节点所在的二项树仍然是一个最小堆,所以需要将改后的点依次与各个祖先节点比较,如果较小需要向上移动,即与父节点交换关键字大小。时间复杂度O(logn)
void BinomialHeap::Decrease_Key(int oldkey, int key) {//减少节点的值
BinomialNode* node;
node = search(mRoot, oldkey);
if (node == NULL)return;
if (search(mRoot, key) != NULL) {//改后的值已经存在
cout << "减小后的值已经存在" << endl;
return;
}
node->key = key;
BinomialNode* leftchild = node;
BinomialNode* parent = node->parent;
while (parent != NULL && leftchild->key < parent->key) {//与比减小后节点大的父节点交换位置
swap(parent->key, leftchild->key);
leftchild = parent;//向父节点遍历
parent = leftchild->parent;
}
}
删除操作函数
(1)将该节点不断向树根遍历,依次交换父节点和子节点,最后到达所在树的根节点,
(2)将该节点所在的二项树从二项堆中移除,将剩余的部分连成新的二项堆。
(3)将该节点所在的树根节点删除,剩下的子树组成新的二项堆(与移除最小节点类似),
(4)最后新二项堆与移除后的原二项堆合并。
时间复杂度为O(logn)
void BinomialHeap::Delete(int key) {
if (mRoot == NULL)return;
BinomialNode* deletenode = search(mRoot, key);//删除的节点
if (deletenode == NULL) {
cout << "没有此节点,无法删除" << endl;
return;
}
BinomialNode* parent = deletenode->parent;
while (parent != NULL) {//将删除的节点放到所在二项树的根节点
swap(deletenode->key, parent->key);
deletenode = parent;
parent = deletenode->parent;
}
BinomialNode* prenode = NULL;
BinomialNode* node = mRoot;
while (node != deletenode) {//更新前一个节点位置
prenode = node;
node = node->next;
}
if (prenode != NULL) {//删除的节不是第一个根节点
prenode->next = deletenode->next;
}
else {//是第一个根节点
mRoot = deletenode->next;
}
mRoot = Union(mRoot, transform(deletenode->leftchild));
delete deletenode;
}
打印操作
遍历根节点,没到达一个二项树的根节点,打印这个树,遍历树,输出每个节点是谁的兄弟节点,或者是谁的孩子节点。
void BinomialHeap::printtree(BinomialNode* node, BinomialNode* prenode, int relationship) {
while (node != NULL) {
if (relationship == 1) {//如果当前节点node是prenode的一个左孩子
printf("\t%2d(%d) is %2d's child\n", node->key, node->degree, prenode->key);
}
else {//当前节点node是prenode的兄弟节点
printf("\t%2d(%d) is %2d's next\n", node->key, node->degree, prenode->key);
}
if (node->leftchild != NULL)printtree(node->leftchild, node, 1);//子树
//兄弟节点
prenode = node;
node = node->next;
relationship = 2;
}
}
void BinomialHeap::printheap() {
if (mRoot == NULL)return;
BinomialNode* node = mRoot;
cout << "二项堆( ";
while (node != NULL) {
cout << "B" << node->degree << " ";
node = node->next;
}
cout << ")的详细信息:" << endl;
int i = 0;
node = mRoot;
while (node != NULL) {
i++;
cout << i << ". 二项树B" << node->degree << ": " << endl;
printf("\t%2d(%d) is root\n", node->key, node->degree);//输出根
printtree(node->leftchild, node, 1);//输出二项树
node = node->next;
}
cout << endl;
}
图形化操作
利用所写的图形化代码和软件graphviz自动生成图,父子关系无向线段直接相连,兄弟节点用有向箭头。
void showBinomialHeap(BinomialHeap* heap, string h) {
ofstream fout(h + ".dot");
fout << "graph g {\n"; // 无向图
function<void(BinomialNode*, BinomialNode*, int)> traverse = [&](BinomialNode* node, BinomialNode* parent, int level) {// 辅助函数,用于递归遍历树
if (node == NULL)
return;
// 若存在下一个节点,则画箭头连接,并且画水平线
if (node->next != NULL) {
fout << node->key << " -- " << node->next->key << " [dir=forward, constraint=false];\n";
fout << "{ rank=same; " << node->key << "; " << node->next->key << "; }\n";
}
// 若存在父节点,则画边连接节点和父节点
if (parent != NULL) {
fout << node->key << " -- " << parent->key << " [dir=none];\n";
}
// 遍历其子节点
if (node->leftchild != NULL) {
traverse(node->leftchild, node, level + 1);
}
// 遍历兄弟节点
traverse(node->next, parent, level);
};
// 从根节点开始遍历
traverse(heap->getRoot(), NULL, 0);
fout << "}\n";
fout.close();
string order = "dot -Tjpg " + h + ".dot -o " + h + ".jpg";
system(order.c_str());
}
主函数中对各种功能测试代码
int main() {
int a[] = { 13,8,24,19,29,35,40 };
int b[] = { 17,34,22,43,4,30,21,5,49,10,26,55,14 };
int alen = sizeof(a) / sizeof(a[0]);
int blen = sizeof(b) / sizeof(b[0]);
BinomialHeap* ha = new BinomialHeap();
BinomialHeap* hb = new BinomialHeap();
//查找最小值
//cout << "二项堆(ha)中依次添加: ";
//for (int i = 0; i < alen; i++) {
// cout << a[i] << " ";
// ha->Insert(a[i]);
//}
//cout << endl;
//int minnum=ha->FindMin();
//cout << "二项堆(ha)最小值为:" << minnum << endl;
//检验插入操作
//cout << "二项堆(ha)中依次添加: ";
//for (int i = 0; i < alen; i++) {
// cout << a[i] << " ";
// ha->Insert(a[i]);
//}
//cout << endl;
//cout << "二项堆(ha)的详细信息:" << endl;
//ha->printheap();
//showBinomialHeap(ha,"ha");
//检验合并操作
// 二项堆ha
//cout << "二项堆(ha)中依次添加:";
// for(int i=0; i<alen; i++){
// cout << a[i] << " ";
// ha->Insert(a[i]);
// }
// cout << endl;
// cout << "二项堆(ha)的详细信息:" << endl;
// ha->printheap(); // 打印二项堆ha
// showBinomialHeap(ha, "ha");
// // 二项堆hb
// cout << "二项堆(hb)中依次添加:" << endl;
// for(int i=0; i<blen; i++){
// cout << b[i] << " ";
// hb->Insert(b[i]);
// }
// cout << endl;
// cout << "二项堆(b)的详细信息: \n";
// hb->printheap(); // 打印二项堆hb
// showBinomialHeap(hb, "hb");
// // 将"二项堆hb"合并到"二项堆ha"中。
// ha->Union(hb);
// cout << "合并a和b后的详细信息:\n";
// ha->printheap();// 打印二项堆ha的详细信息
// showBinomialHeap(ha, "Union");
//检验减少节点的值
// 二项堆hb
//cout << "二项堆中依次添加:";
// for(int i=0; i<blen; i++){
// cout << b[i] << " ";
// hb->Insert(b[i]);
// }
//cout << endl;
//cout << "二项堆(hb)的详细信息: \n";
//hb->printheap(); // 打印二项堆hb
//showBinomialHeap(hb, "hb");
// // 将节点22更新为2
//hb->Decrease_Key(22, 2);
//cout << "更新节点22->2后的详细信息: \n";
//hb->printheap(); // 打印二项堆hb
//showBinomialHeap(hb, "DecreaseKey");
//检验删除操作
// 二项堆hb
//cout << "二项堆(hb)中依次添加:";
//for (int i = 0; i < blen; i++){
// cout << b[i] << " ";
// hb->Insert(b[i]);
//}
//cout << endl;
//cout << "二项堆(hb)的详细信息: \n";
//hb->printheap(); // 打印二项堆hb
//showBinomialHeap(hb, "hb");
删除二项堆hb中的节点
//int i = 22;
//hb->Delete(i);
//cout << "删除节点" << i << "后的详细信息: \n";
//hb->printheap(); // 打印二项堆hb
//showBinomialHeap(hb, "Delete");
//检验删除最小值操作
cout << "二项堆(ha)中依次添加:";
for (int i = 0; i < alen; i++){
cout << a[i] << " ";
ha->Insert(a[i]);
}
cout << endl;
cout << "二项堆(ha)的详细信息: \n";
ha->printheap(); // 打印二项堆hb
showBinomialHeap(ha, "ha");
// 删除二项堆hb中最小的节点
ha->Extract_Min();
cout << "删除最小节点后的详细信息: \n";
ha->printheap(); // 打印二项堆hb
showBinomialHeap(ha, "ExtractMin");
return 0;
}
测试结果
(1)查找最小值
(2)插入操作
(3)合并操作
(4)减少某一节点的值
(5)删除某一节点
(6)删除最小节点
复杂度分析
合并操作二项堆比普通堆的时间复杂度更低。二叉堆查找最小节点的时间复杂度比一般二叉堆要大。
完整代码(含注释)
#include<iostream>
#include<string>
#include<fstream>
#include<vector>
#include<map>
#include <functional>
using namespace std;
struct BinomialNode {
int key;//关键字
int degree;//度数
BinomialNode* leftchild;//左孩子
BinomialNode* parent;//父节点
BinomialNode* next;//兄弟节点
BinomialNode(int k) :key(k), degree(0), leftchild(NULL), parent(NULL), next(NULL) {}
};
class BinomialHeap {
private:
BinomialNode* mRoot;//根节点
public:
BinomialHeap() :mRoot(NULL) {}
~BinomialHeap() {}
BinomialNode* getRoot() {
return mRoot;
}
BinomialNode* MakeNode(int Key) {//构造
BinomialNode* node;
node = new BinomialNode(Key);
return node;
}
BinomialNode* search(BinomialNode* heap, int key) {//查找是否存在
BinomialNode* leftchild;
BinomialNode* parent = heap;
while (parent != NULL) {
if (parent->key == key) {
return parent;
}
else {
leftchild = search(parent->leftchild, key);//在子树里找
if (leftchild != NULL) {//找到
return leftchild;
}
parent = parent->next;//下一个子树
}
}
return NULL;
}
int FindMin() {//找最小节点值
BinomialNode* fin = mRoot->next;
BinomialNode* minnode = mRoot;
while (fin != NULL) {
if (fin->key < minnode->key)minnode = fin;
fin = fin->next;
}
return minnode->key;
}
BinomialNode* findmin() {//找最小节点
if (mRoot == NULL)return NULL;
BinomialNode* fin = mRoot->next;
BinomialNode* minnode = mRoot;
while (fin != NULL) {
if (fin->key < minnode->key)minnode = fin;
fin = fin->next;
}
return minnode;
}
BinomialNode* unionroot(BinomialNode* h1, BinomialNode* h2) {//合并两个根节点链表,按度数递增合并
if (h1 == NULL) return h2;
else if (h2 == NULL) return h1;
BinomialNode* node = NULL;
if (h1->degree < h2->degree) {//找度数较小的
node = h1;
node->next = unionroot(h1->next, h2);
}
else {
node = h2;
node->next = unionroot(h2->next, h1);
}
return node;
}
void link(BinomialNode* leftchild, BinomialNode* heap) {//两个相邻的度数相同的二项树树合并成新二项树
leftchild->parent = heap;
leftchild->next = heap->leftchild;
heap->leftchild = leftchild;
heap->degree++;
}
BinomialNode* Union(BinomialNode* h1, BinomialNode* h2);
void Union(BinomialHeap* other) {//将另一个队与本堆合并
if (other != NULL && other->mRoot != NULL) mRoot = Union(mRoot, other->mRoot);
}
void Insert(int key);
BinomialNode* transform(BinomialNode* heap) {//将子树转换成二项堆,反转二项堆heap
BinomialNode* next;
BinomialNode* pre = NULL;//原来的前一个节点
if (heap==NULL)return heap;
heap->parent = NULL;
while (heap->next!=NULL) {
next = heap->next;
heap->next = pre;
pre = heap;
heap = next;
heap->parent = NULL;
}
heap->next = pre;
return heap;
}
void Extract_Min();
void Decrease_Key(int oldkey, int key);
void Delete(int key);
void printtree(BinomialNode* node, BinomialNode* prenode, int relationship);
void printheap();
};
BinomialNode* BinomialHeap::Union(BinomialNode* h1, BinomialNode* h2) {//合并
BinomialNode* heap;
BinomialNode* pre_x, * x, * next_x;
heap = unionroot(h1, h2);//h1,h2合并成一个按度数递增的链表
if (heap == NULL)return NULL;
pre_x = NULL;
x = heap;
next_x = x->next;
while (next_x != NULL) {//遍历所有二项树的根
if ((x->degree != next_x->degree) || ((next_x->next != NULL) && (next_x->degree == next_x->next->degree))) {//和下一节点度数不同。或者和下两个度数相等的时候(要找到最后度数相同的)
pre_x = x;//不操作,直接向下一节点
x = next_x;
}
else if (x->key <= next_x->key) {//本节点的关键字小
x->next = next_x->next;
link(next_x, x);//x为next_x的父节点
}
else {//本届点关键字大
if (pre_x == NULL) {//如果此节点为第一个根节点
heap = next_x;
}
else {
pre_x->next = next_x;
}
link(x, next_x);//x作为next_x的子节点
x = next_x;
}
next_x = x->next;
}
return heap;
}
void BinomialHeap::Insert(int key) {//插入
if (search(mRoot, key) != NULL) {
cout << "无法插入关键字" << key << "已存在" << endl;
return;
}
BinomialNode* node1;
node1 = MakeNode(key);
if (node1 == NULL)return;
mRoot = Union(mRoot, node1);
}
void BinomialHeap::Extract_Min() {//移除最小值
if (mRoot == NULL) return;
BinomialNode* root = mRoot;
BinomialNode* minnode = findmin();//找到最小值所在的节点
BinomialNode* pre, * pos;
pre = NULL;
pos = root;
while (pos != minnode) {//pre为最小节点前的节点
pre = pos;
pos = pos->next;
}
if (pre) {//最小节点不是第一个根节点
pre->next = minnode->next;
}
else {//最小节点是第一个根节点
root = minnode->next;
}
root = Union(root, transform(minnode->leftchild));//将删掉最小节点的堆和原堆合并
delete minnode;
mRoot = root;
}
void BinomialHeap::Decrease_Key(int oldkey, int key) {//减少节点的值
BinomialNode* node;
node = search(mRoot, oldkey);
if (node == NULL)return;
if (search(mRoot, key) != NULL) {//改后的值已经存在
cout << "减小后的值已经存在" << endl;
return;
}
node->key = key;
BinomialNode* leftchild = node;
BinomialNode* parent = node->parent;
while (parent != NULL && leftchild->key < parent->key) {//与比减小后节点大的父节点交换位置
swap(parent->key, leftchild->key);
leftchild = parent;//向父节点遍历
parent = leftchild->parent;
}
}
void BinomialHeap::Delete(int key) {
if (mRoot == NULL)return;
BinomialNode* deletenode = search(mRoot, key);//删除的节点
if (deletenode == NULL) {
cout << "没有此节点,无法删除" << endl;
return;
}
BinomialNode* parent = deletenode->parent;
while (parent != NULL) {//将删除的节点放到所在二项树的根节点
swap(deletenode->key, parent->key);
deletenode = parent;
parent = deletenode->parent;
}
BinomialNode* prenode = NULL;
BinomialNode* node = mRoot;
while (node != deletenode) {//更新前一个节点位置
prenode = node;
node = node->next;
}
if (prenode != NULL) {//删除的节不是第一个根节点
prenode->next = deletenode->next;
}
else {//是第一个根节点
mRoot = deletenode->next;
}
mRoot = Union(mRoot, transform(deletenode->leftchild));
delete deletenode;
}
void BinomialHeap::printtree(BinomialNode* node, BinomialNode* prenode, int relationship) {
while (node != NULL) {
if (relationship == 1) {//如果当前节点node是prenode的一个左孩子
printf("\t%2d(%d) is %2d's child\n", node->key, node->degree, prenode->key);
}
else {//当前节点node是prenode的兄弟节点
printf("\t%2d(%d) is %2d's next\n", node->key, node->degree, prenode->key);
}
if (node->leftchild != NULL)printtree(node->leftchild, node, 1);//子树
//兄弟节点
prenode = node;
node = node->next;
relationship = 2;
}
}
void BinomialHeap::printheap() {
if (mRoot == NULL)return;
BinomialNode* node = mRoot;
cout << "二项堆( ";
while (node != NULL) {
cout << "B" << node->degree << " ";
node = node->next;
}
cout << ")的详细信息:" << endl;
int i = 0;
node = mRoot;
while (node != NULL) {
i++;
cout << i << ". 二项树B" << node->degree << ": " << endl;
printf("\t%2d(%d) is root\n", node->key, node->degree);//输出根
printtree(node->leftchild, node, 1);//输出二项树
node = node->next;
}
cout << endl;
}
void showBinomialHeap(BinomialHeap* heap, string h) {
ofstream fout(h + ".dot");
fout << "graph g {\n"; // 无向图
function<void(BinomialNode*, BinomialNode*, int)> traverse = [&](BinomialNode* node, BinomialNode* parent, int level) {// 辅助函数,用于递归遍历树
if (node == NULL)
return;
// 若存在下一个节点,则画箭头连接,并且画水平线
if (node->next != NULL) {
fout << node->key << " -- " << node->next->key << " [dir=forward, constraint=false];\n";
fout << "{ rank=same; " << node->key << "; " << node->next->key << "; }\n";
}
// 若存在父节点,则画边连接节点和父节点
if (parent != NULL) {
fout << node->key << " -- " << parent->key << " [dir=none];\n";
}
// 遍历其子节点
if (node->leftchild != NULL) {
traverse(node->leftchild, node, level + 1);
}
// 遍历兄弟节点
traverse(node->next, parent, level);
};
// 从根节点开始遍历
traverse(heap->getRoot(), NULL, 0);
fout << "}\n";
fout.close();
string order = "dot -Tjpg " + h + ".dot -o " + h + ".jpg";
system(order.c_str());
}
int main() {
int a[] = { 13,8,24,19,29,35,40 };
int b[] = { 17,34,22,43,4,30,21,5,49,10,26,55,14 };
int alen = sizeof(a) / sizeof(a[0]);
int blen = sizeof(b) / sizeof(b[0]);
BinomialHeap* ha = new BinomialHeap();
BinomialHeap* hb = new BinomialHeap();
//查找最小值
//cout << "二项堆(ha)中依次添加: ";
//for (int i = 0; i < alen; i++) {
// cout << a[i] << " ";
// ha->Insert(a[i]);
//}
//cout << endl;
//int minnum=ha->FindMin();
//cout << "二项堆(ha)最小值为:" << minnum << endl;
//检验插入操作
//cout << "二项堆(ha)中依次添加: ";
//for (int i = 0; i < alen; i++) {
// cout << a[i] << " ";
// ha->Insert(a[i]);
//}
//cout << endl;
//cout << "二项堆(ha)的详细信息:" << endl;
//ha->printheap();
//showBinomialHeap(ha,"ha");
//检验合并操作
// 二项堆ha
//cout << "二项堆(ha)中依次添加:";
// for(int i=0; i<alen; i++){
// cout << a[i] << " ";
// ha->Insert(a[i]);
// }
// cout << endl;
// cout << "二项堆(ha)的详细信息:" << endl;
// ha->printheap(); // 打印二项堆ha
// showBinomialHeap(ha, "ha");
// // 二项堆hb
// cout << "二项堆(hb)中依次添加:" << endl;
// for(int i=0; i<blen; i++){
// cout << b[i] << " ";
// hb->Insert(b[i]);
// }
// cout << endl;
// cout << "二项堆(b)的详细信息: \n";
// hb->printheap(); // 打印二项堆hb
// showBinomialHeap(hb, "hb");
// // 将"二项堆hb"合并到"二项堆ha"中。
// ha->Union(hb);
// cout << "合并a和b后的详细信息:\n";
// ha->printheap();// 打印二项堆ha的详细信息
// showBinomialHeap(ha, "Union");
//检验减少节点的值
// 二项堆hb
//cout << "二项堆中依次添加:";
// for(int i=0; i<blen; i++){
// cout << b[i] << " ";
// hb->Insert(b[i]);
// }
//cout << endl;
//cout << "二项堆(hb)的详细信息: \n";
//hb->printheap(); // 打印二项堆hb
//showBinomialHeap(hb, "hb");
// // 将节点22更新为2
//hb->Decrease_Key(22, 2);
//cout << "更新节点22->2后的详细信息: \n";
//hb->printheap(); // 打印二项堆hb
//showBinomialHeap(hb, "DecreaseKey");
//检验删除操作
// 二项堆hb
//cout << "二项堆(hb)中依次添加:";
//for (int i = 0; i < blen; i++){
// cout << b[i] << " ";
// hb->Insert(b[i]);
//}
//cout << endl;
//cout << "二项堆(hb)的详细信息: \n";
//hb->printheap(); // 打印二项堆hb
//showBinomialHeap(hb, "hb");
删除二项堆hb中的节点
//int i = 22;
//hb->Delete(i);
//cout << "删除节点" << i << "后的详细信息: \n";
//hb->printheap(); // 打印二项堆hb
//showBinomialHeap(hb, "Delete");
//检验删除最小值操作
cout << "二项堆(ha)中依次添加:";
for (int i = 0; i < alen; i++){
cout << a[i] << " ";
ha->Insert(a[i]);
}
cout << endl;
cout << "二项堆(ha)的详细信息: \n";
ha->printheap(); // 打印二项堆hb
showBinomialHeap(ha, "ha");
// 删除二项堆hb中最小的节点
ha->Extract_Min();
cout << "删除最小节点后的详细信息: \n";
ha->printheap(); // 打印二项堆hb
showBinomialHeap(ha, "ExtractMin");
return 0;
}
样例设计与算样例设计与算法设计思路法设计思路