///heap.h~ #ifndef _HEAP_H #define _HEAP_H class BinQueue { private: int currentSize;//当前结点数目 int maxSize;//树的最多个数 struct BinNode { int element; //结点的元素 BinNode *parent; BinNode *child; //左孩子 BinNode *sibling; //右邻居 BinNode(int x = 0, BinNode* p = nullptr);//结点构造函数 };//结点 BinNode** BinTree; //二项树 BinNode* combineTrees(BinNode* t1, BinNode* t2); //连接两棵树 void traversal(BinNode* p); //前序遍历 BinNode* findKey(BinNode* h, int key); BinQueue(int size,int current);//二项队列构造函数 void destroy(BinNode* h); public: BinQueue(int size = 10); //二项队列构造函数 ~BinQueue(); void insert(int x);//插入 void findAndDecreaseKey(int k,int x);//找到值为x的关键字并减为k void decreaseKey(int k, BinNode* x);//减少关键字为k void merge(const BinQueue &h); //合并 int deleteMin(); //删除最小值 void print();//二项队列的输出 }; #endif
#include"heap.h" #include<algorithm> #include<cstdio> #include<climits> extern double sum1; BinQueue::BinNode::BinNode(int x,BinNode* p) { parent = p; element = x;//存储结点 child = sibling = nullptr; } BinQueue::~BinQueue() { for (int i = 0; i < maxSize; i++) { if (BinTree[i]) { destroy(BinTree[i]); } } } void BinQueue::destroy(BinNode* h) { BinNode* child = h->child; BinNode* sibling = h->sibling; if(child)destroy(child); if(sibling)destroy(sibling); delete h; } //私有的构造函数,不为用户提供指定结点大小的方法 BinQueue::BinQueue(int size,int current) { currentSize = current; //存储当前结点数 maxSize = size; //存储树的最大个数 BinTree = new BinNode*[size]; //为二项树申请内存 for (int i = 0; i < size; i++) { BinTree[i] = nullptr; //初始化每个树的根节点为null } } //构造函数,传入二项队列的大小 BinQueue::BinQueue(int size) { currentSize = 0; //存储当前结点数(初始为0) maxSize = size; //存储树的最大个数 BinTree = new BinNode*[size]; //为二项树申请内存 for (int i = 0; i < size; i++) { BinTree[i] = nullptr; //初始化每个树的根节点为null } } //找到关键字并减小(接口函数) //attention:统计时间时,find的时间不要计入 void BinQueue::findAndDecreaseKey(int k, int x) { BinNode* tmp = nullptr; for (int i = 0; i < maxSize; i++) { if (BinTree[i]) { tmp = findKey(BinTree[i], x); //搜索每一棵不为空的二项树 if (tmp)break; //找到了退出循环 } } if (tmp) { //如果存在,执行减少关键字的步骤; decreaseKey(k, tmp); } } //找到关键字 BinQueue::BinNode* BinQueue::findKey(BinNode* h, int key) { BinNode* p, *x = nullptr; p = h; while (p) { //当p不为空时 if (p->element == key)return p; //当前结点的值为key,返回当前结点 else { x = findKey(p->child, key); //否则继续搜索其孩子 if (x) return x;//在它的后代找到了关键字,返回其后代 p = p->sibling; // 继续搜索其兄弟 } } return nullptr;//没找到,则返回null } //减小关键字 void BinQueue::decreaseKey(int k, BinNode* x) { if (k >= x->element) return;//修改的值比原来大,不符合条件 x->element = k;//更新关键字 BinNode* now = x; BinNode* parent = now->parent; //记录当前结点及其父节点 //父节点不为空,且父节点值大于当前结点时(违背了最小堆性质) while (parent != nullptr && now->element < parent->element) { std::swap(parent->element, now->element);//交换两者 now = parent;//继续向上寻找 parent = now->parent; } return; } //插入 void BinQueue::insert(int x) { if (currentSize == 0) { //如果没有结点,直接插入结点 BinTree[0] = new BinNode(x); currentSize++; return; } //如果已经有结点,新建一个只含一个结点的二项队列,与其合并 BinQueue *tmp = new BinQueue(maxSize,1); tmp->BinTree[0] = new BinNode(x); merge(*tmp); return; } //合并 void BinQueue::merge(const BinQueue &h) { int i, j; BinNode *t1, *t2; BinNode *carry = nullptr;//carry代表进位 currentSize += h.currentSize;//更新当前结点数 for (i = 0, j = 1; j <= currentSize; i++, j *= 2) { t1 = BinTree[i]; t2 = h.BinTree[i]; // 将三棵树的状态表示为二进制的形式 // | carry | t2 | t1 | // null为0,非null为1 // 总共有7种可能结果 switch ((!!carry)*4 + (!!t2)*2 + !!t1) { case 0:/*000*/ case 1:/*001*/ break; case 2:/*010*/ BinTree[i] = t2, h.BinTree[i] = nullptr; break; case 3:/*011*/ carry = combineTrees(t1, t2); h.BinTree[i] = BinTree[i] = nullptr; break; case 4:/*100*/ BinTree[i] = carry, carry = nullptr; break; case 5:/*101*/ carry = combineTrees(t1, carry); BinTree[i] = nullptr; break; case 6:/*110*/ carry = combineTrees(t2, carry); h.BinTree[i] = nullptr; break; case 7:/*111*/ BinTree[i] = carry; carry = combineTrees(t1, t2); h.BinTree[i] = nullptr; break; } } } //连接 BinQueue::BinNode* BinQueue::combineTrees(BinNode* t1, BinNode* t2) { //维护t1始终小于t2,保证t1为根节点,t2是其孩子 if (t1->element > t2->element) { std::swap(t1, t2); } t2->parent = t1; //t2在成为t1的孩子之前,t1的孩子先成为它的兄弟 t2->sibling = t1->child; //t2再成为t1的孩子 t1->child = t2; return t1; } //输出二项队列 void BinQueue::print() { for (int i = 0; i < maxSize; i++) { if (BinTree[i] != nullptr) {//不为空的话 printf("Tree %d: ",i); traversal(BinTree[i]);//输出其元素 printf("\n"); } } printf("\n"); return; } //前序遍历 void BinQueue::traversal(BinNode* p) { if (!p)return; if (!p->parent) printf("(%d null),",p->element); else printf("(%d %d),", p->element,p->parent->element); traversal(p->child);//继续搜索孩子 traversal(p->sibling);//继续搜索邻居 } int BinQueue::deleteMin() { int i, j, minTree; BinNode* deleteTree = nullptr; BinNode* oldRoot = nullptr; int min = INT_MAX; if (currentSize == 0)return -1; //找到最小的树,记录最小元素及其下标 for (i = 0; i < maxSize; i++) { if (BinTree[i] && BinTree[i]->element < min) { min = BinTree[i]->element; minTree = i; } } deleteTree = BinTree[minTree];//临时存储要删除的那个结点 BinTree[minTree] = nullptr;//删除结点 oldRoot = deleteTree; //记录要删除的结点 deleteTree = deleteTree->child; //跟踪要删除结点的孩子结点 free(oldRoot);//释放删除结点的内存 BinQueue deleteQueue(maxSize,((1 << minTree) - 1)); //被删除后树的大小为2^minTree - 1,其中minTree为其下标 //当一个树被删除根结点后,相当于100…000 -> 11…111 //所以只需要去除根结点的子树一个个后移即可 for (j = minTree - 1; j >= 0; j--) { deleteTree->parent = nullptr; deleteQueue.BinTree[j] = deleteTree; deleteTree = deleteTree->sibling; deleteQueue.BinTree[j]->sibling = nullptr; } currentSize -= deleteQueue.currentSize + 1;//更新删除结点后二项队列的结点数 merge(deleteQueue);//将删除结点的子树与原二项队列合并 return min; }