0.二叉堆
排序和顺序统计学中的堆排序所用到的堆,是一棵完全二叉树。存放时利用了二叉树,父亲节点序号(i),子节点序号(2*i,2*i+1)的性质
堆的建立,调整,输出实例如下:#include<iostream> #include<vector> #include<algorithm> using namespace std; //root从data[1]开始存起 //我想到的是左右孩子先比较,返回大孩子序号。大孩子再与父节点比较,如果不调换,调整结束,直接返回;如果调换,调换后,再将大孩子作为序号递归调用调整函数。但他使用迭代似乎可以更加的简洁 //大顶堆 void heapAdjust(vector<int> &data,int s,int l)//data为数据源,s为需要调整的节点 ,l为有效节点个数 { int rc=data[s];//要调整的数值 int i=s; while(i<=l/2) { s=i;//此时s已经作为一个temp,临时的辅助变量 if(data[2*i]<data[2*i+1])//左孩子的数值小于右孩子的数值 { i=2*i+1;//我要右孩子 } else { i=2*i;//我要左孩子 } if(rc>data[i])//父节点数值大于大孩子数值 { return;//无需调整,结束堆调整 } else { swap(data[s],data[i]);//小于则交换父节点和子节点的数值 } } } void heapMake(vector<int> &data,int l) { for(int i=l/2;i>=1;i--) { heapAdjust(data,i,l); } } void heapOutput(vector<int> &data,int l) { for(int i=0;i<l;i++) { cout<<data[1]<<"\t"; data[1]=-1; swap(data[1],data[l-i]); heapAdjust(data,1,l-i-1); } } int main() { vector<int> data; int l; cin>>l; data.push_back(-1); for(int i=0;i<l;i++) { int temp; cin>>temp; data.push_back(temp); } if(l%2==0) { data.push_back(-1); } heapMake(data,l); heapOutput(data,l); }
1.二项堆
a.二项树
定义:
二项树Bk是一种递归定义的有序树。二项树B0只包含一个结点。二项树Bk由两个子树Bk-1连接而成:其中一棵树的根是另一棵树的根的最左孩子。
性质:
1)共有2的k次方个结点;
2)树的高度为k;
3)在深度i处恰有(上k,下i)(因此叫二项树)个结点,其中i=0,...,k;
4)根的度数为k,它大于任何其他结点的度数,并且,如果对根的子女从左到右编号为k-1,k-2,...,0,子女i是子树Bi的根。
b.二项堆
定义:由一组二项树组成,同时满足以下两个性质
1)H中的每个二项树遵循最小堆的性质:结点的关键字大于等于其父结点的关键字。
2)对于任意非负整数k,在H中至多有一棵二项树的根具有度数k。(如下图对于非负整数0,1,2,3,至多只有一棵度数为0,1,2,3的二项树)
存储模型:
struct heapNode { int key;//该节点的值 int degree;//当前节点作为根节点的树的度数,也即k heapNode *parent;//父亲节点,对于每棵树的根节点而言没有父亲节点,值为NULL heapNode *chlid;//该树的第一个孩子 heapNode *sibling;//串联同一父节点下的所有子节点 heapNode(int x):key(x),degree(0),parent(NULL),child(NULL),sibling(NULL){} }; class heap { private: heapNode *head; heapNode *min; };
c.二项堆的建立,插入,删除,合并等#include<iostream> #include<queue> #include<stack> #include<algorithm> using namespace std; //析构函数和复制构造函数不再重载 struct heapNode { int key;//该节点的值,为方便操作,关键字均大于0 int degree;//当前节点作为根节点的树的度数,也即k heapNode *parent;//父亲节点,对于每棵树的根节点而言没有父亲节点,值为NULL heapNode *child;//该树的第一个孩子 heapNode *sibling;//串联同一父节点下的所有子节点 heapNode(int x):key(x),degree(0),parent(NULL),child(NULL),sibling(NULL){} }; struct heap { heapNode *root; heapNode *min; heap():root(NULL),min(NULL){} }; /***********************堆合并的辅助函数*************以下*********/ void mergeSort(heapNode *p,heapNode *q,heapNode *m) { while(p!=NULL&&q!=NULL) { if(p->degree<=q->degree) { m->sibling=p; m=m->sibling; p=p->sibling; } else { m->sibling=q; m=m->sibling; q=q->sibling; } } while(p!=NULL) { m->sibling=p; m=m->sibling; p=p->sibling; } while(q!=NULL) { m->sibling=q; m=m->sibling; q=q->sibling; } } void link(heapNode *root,heapNode *child) { child->sibling=root->child; child->parent=root; root->child=child; root->degree++; } void adjustMin(heapNode *root) { while(root->child!=NULL) { if(root->key>root->child->key) { swap(root->key,root->child->key); root=root->child; } else { return; } } } void adjustD(heapNode *m,heapNode *mpre) { if(m->sibling==NULL) { return; } if(m->degree>m->sibling->degree) { heapNode *temp=m->sibling; m->sibling=temp->sibling; temp->sibling=m; mpre->sibling=temp; mpre=temp; } } void heapMin(heap *h) { heapNode *root=h->root; int minkey=root->key; h->min=root; while(root->sibling!=NULL) { if(root->sibling->key<minkey) { minkey=root->sibling->key; h->min=root->sibling; } root=root->sibling; } } /***********************堆合并的辅助函数***************以上********/ //合并两个堆 void unionHeap(heap *a,heap *b) { heapNode *p=a->root; heapNode *q=b->root; if(p==NULL) { a->root=b->root;//仅在该题中,c指向的那块内存可以是b指向的那块,没必要要重载复制构造函数 return; } if(q==NULL) { return; } heapNode *t1=new heapNode(-1);//为方便计算所设的头结点 heapNode *m=t1; mergeSort(p,q,m); m=t1->sibling; heapNode *mpre=t1; while(m->sibling!=NULL) { if(m->degree!=m->sibling->degree) { m=m->sibling; mpre=mpre->sibling; } else { heapNode *temp=m->sibling; m->sibling=temp->sibling; link(m,temp);//链接两个堆的根 adjustMin(m);//调整,保持每棵二项树树根最小的性质 adjustD(m,mpre);//调整,保证度数小的二项树在前,以防止间隔的两棵二项树具有相同的度,却得不到合并的现象 } } a->root=t1->sibling; heapMin(a); return; } //遍历(遍历函数有误) void levelTraversal(heap *r) { queue<heapNode*> q; q.push(r->root); while(!q.empty()) { heapNode *root=q.front(); q.pop(); if(root->key>=0) { cout<<"key("<<root->key<<") degree("<<root->degree<<")"<<"\t"; } if(root->child!=NULL) { q.push(root->child); } if(root->sibling!=NULL) { q.push(root->sibling); } } cout<<endl; } //插入一个节点,就先构造一个只有一个节点的二项堆,然后在和原二项堆合并 void insertNode(int x,heap *h) { heap *temp=new heap(); heapNode *t=new heapNode(x); temp->root=t; temp->min=t; unionHeap(h,temp); } /***********************删除关键字的辅助函数***************以下********/ //删除有最小关键字的节点 void deleteMinKeyNode(heap *h) { //把含最小(根)节点的那棵二项树给拎出来 heapNode *temp; if(h->root==h->min) { temp=h->root; h->root=temp->sibling; } else { heapNode *pre=h->root; while(pre->sibling!=h->min) { pre=pre->sibling; } temp=pre->sibling; pre->sibling=temp->sibling; } //删除最小节点 heapNode *dele=temp; temp=temp->child; delete dele; if(temp==NULL)//无孩子,度为0,直接返回 { return; } //把最小节点原来的孩子逆序排列 stack<heapNode*> s; while(temp!=NULL) { heapNode *tempbother=temp->sibling; temp->parent=NULL; temp->sibling=NULL; s.push(temp); temp=tempbother; } //组成新堆 heap *t=new heap(); t->root=s.top(); s.pop(); temp=t->root; while(!s.empty()) { temp->sibling=s.top(); temp=temp->sibling; s.pop(); } //新堆与原堆合并 unionHeap(h,t); heapMin(h); } //减少关键字的值成负值-1 void decreaseKey(heap *h,int key) { heapNode *temp; queue<heapNode*> q; q.push(h->root); while(!q.empty()) { heapNode *root=q.front(); q.pop(); if(root->key==key) { temp=root; break; } if(root->child!=NULL) { q.push(root->child); } if(root->sibling!=NULL) { q.push(root->sibling); } } temp->key=-1; while(temp->parent!=NULL) { swap(temp->parent->key,temp->key); temp=temp->parent; } heapMin(h); } /***********************删除关键字的辅助函数***************以上********/ //删除关键字 void deleteKey(heap *h,int key) { //先减少关键字的值,使它达到根节点 decreaseKey(h,key); //然后删除这个最小根节点 deleteMinKeyNode(h); } int main() { heap *h=new heap(); heapNode *ht0=new heapNode(11); h->root=ht0; h->min=ht0; insertNode(12,h); insertNode(13,h); heap *h1=new heap(); heapNode *ht1=new heapNode(21); h1->root=ht1; h1->min=ht1; insertNode(22,h1); insertNode(23,h1); unionHeap(h,h1); levelTraversal(h); deleteKey(h,23); levelTraversal(h); }
3.斐波那契堆
类似于平衡树-红黑树与高度自平衡的AVL的关系。斐波那契堆较二项堆更为松散,除抽取最小元素和删除操作,其他操作的平摊时间复杂度在O(1)
a.斐波那契堆与二项堆的异同:
同:1、都是由有序树构成。2、每棵有序树均具有最小堆性质。3.斐波那契堆中的树不一定是二项树
异:1、斐波那契堆无论是有序树之间,还是有序树孩子均是由双向循环链表串联起来,已达到插入等操作的O(1)。2、斐波那契堆中,各棵树的度可以有重叠,并且并不需要像二项堆那样按度的大小对二项树进行排列。3、斐波那契堆的插入并不像二项堆那样,插入一个节点就合并一次,为减小频繁合并的消耗,斐波那契堆把合并操作推迟到删除一个节点时再做。4、斐波那契堆的根节点就是关键字最小的节点,而不是像二项堆那样根节点是度数最小(大)的节点
b.存储模型
c.斐波那契堆的插入、删除、合并等环形双链表的两个好处:1、可以在O(1)的时间内删除某个节点;2、也可以在O(1)的时间内插入某个节点。struct Node { Node *lbother; Node *rbother; Node *parent; Node *child; int key; int degree; bool mark;//是否被删除过孩子 Node(int x):key(x),degree(0),mark(false),lbother(NULL),rbother(NULL),parent(NULL),child(NULL){} }; struct Heap { int n;//堆中节点个数 Node *min;//最小节点,同时作为根节点 Heap():min(NULL),n(0){} };
mark标记的作用是用来标记“该节点的子节点是否有被删除过”,它的作用是来实现级联剪切。而级联剪切的真正目的是为了防止'最小堆'由树演化成链表(无节制的删孩子,删的只剩一个时,就成了链表)。
斐波那契堆代码不是自己编的来自:http://dsqiu.iteye.com/blog/1714961
//说明: //代码中Fibonacci Heap 用变量heap表示 //结点通常用x,y等表示 #include<iostream> #include<cstdio> #include<cstdlib> #include<cmath> #include<climits> using namespace std; //斐波那契结点ADT struct FibonacciHeapNode { int key; //结点 int degree; //度 FibonacciHeapNode * left; //左兄弟 FibonacciHeapNode * right; //右兄弟 FibonacciHeapNode * parent; //父结点 FibonacciHeapNode * child; //第一个孩子结点 bool marked; //是否被删除第1个孩子 }; typedef FibonacciHeapNode FibNode; //斐波那契堆ADT struct FibonacciHeap { int keyNum; //堆中结点个数 FibonacciHeapNode * min;//最小堆,根结点 int maxNumOfDegree; //最大度 FibonacciHeapNode * * cons;//指向最大度的内存区域 }; typedef FibonacciHeap FibHeap; /*****************函数申明*************************/ //将x从双链表移除 inline void FibNodeRemove(FibNode * x); //将x堆结点加入y结点之前(循环链表中) void FibNodeAdd(FibNode * x, FibNode * y); //初始化一个空的Fibonacci Heap FibHeap * FibHeapMake() ; //初始化结点x FibNode * FibHeapNodeMake(); //堆结点x插入fibonacci heap中 void FibHeapInsert(FibHeap * heap, FibNode * x); //将数组内的值插入Fibonacci Heap void FibHeapInsertKeys(FibHeap * heap, int keys[], int keyNum); //将值插入Fibonacci Heap static void FibHeapInsertKey(FibHeap * heap, int key); //抽取最小结点 FibNode * FibHeapExtractMin(FibHeap * heap); //合并左右相同度数的二项树 void FibHeapConsolidate(FibHeap * heap); //将x根结点链接到y根结点 void FibHeapLink(FibHeap * heap, FibNode * x, FibNode *y); //开辟FibHeapConsolidate函数哈希所用空间 static void FibHeapConsMake(FibHeap * heap); //将堆的最小结点移出,并指向其有兄弟 static FibNode *FibHeapMinRemove(FibHeap * heap); //减小一个关键字 void FibHeapDecrease(FibHeap * heap, FibNode * x, int key); //切断x与父节点y之间的链接,使x成为一个根 static void FibHeapCut(FibHeap * heap, FibNode * x, FibNode * y); //级联剪切 static void FibHeapCascadingCut(FibHeap * heap, FibNode * y); //修改度数 void renewDegree(FibNode * parent, int degree); //删除结点 void FibHeapDelete(FibHeap * heap, FibNode * x); //堆内搜索关键字 FibNode * FibHeapSearch(FibHeap * heap, int key); //被FibHeapSearch调用 static FibNode * FibNodeSearch(FibNode * x, int key); //销毁堆 void FibHeapDestory(FibHeap * heap); //被FibHeapDestory调用 static void FibNodeDestory(FibNode * x); //输出打印堆 static void FibHeapPrint(FibHeap * heap); //被FibHeapPrint调用 static void FibNodePrint(FibNode * x); /************************************************/ //将x从双链表移除 inline void FibNodeRemove(FibNode * x) { x->left->right = x->right; x->right->left = x->left; } /* 将x堆结点加入y结点之前(循环链表中) a …… y a …… x …… y */ inline void FibNodeAdd(FibNode * x, FibNode * y) { x->left = y->left; y->left->right = x; x->right = y; y->left = x; } //初始化一个空的Fibonacci Heap FibHeap * FibHeapMake() { FibHeap * heap = NULL; heap = (FibHeap *) malloc(sizeof(FibHeap)); if (NULL == heap) { puts("Out of Space!!"); exit(1); } memset(heap, 0, sizeof(FibHeap)); return heap; } //初始化结点x FibNode * FibHeapNodeMake() { FibNode * x = NULL; x = (FibNode *) malloc(sizeof(FibNode)); if (NULL == x) { puts("Out of Space!!"); exit(1); } memset(x, 0, sizeof(FibNode)); x->left = x->right = x; return x; } //堆结点x插入fibonacci heap中 void FibHeapInsert(FibHeap * heap, FibNode * x) { if (0 == heap->keyNum) { heap->min = x; } else { FibNodeAdd(x, heap->min); x->parent = NULL; if (x->key < heap->min->key) { heap->min = x; } } heap->keyNum++; } //将数组内的值插入Fibonacci Heap void FibHeapInsertKeys(FibHeap * heap, int keys[], int keyNum) { for (int i = 0; i < keyNum; i++) { FibHeapInsertKey(heap, keys[i]); } } //将值插入Fibonacci Heap static void FibHeapInsertKey(FibHeap * heap, int key) { FibNode * x = NULL; x = FibHeapNodeMake(); x->key = key; FibHeapInsert(heap, x); } //抽取最小结点 FibNode * FibHeapExtractMin(FibHeap * heap) { FibNode * x = NULL, * z = heap->min; if (z != NULL) { //删除z的每一个孩子 while (NULL != z->child) { x = z->child; FibNodeRemove(x); if (x->right == x) { z->child = NULL; } else { z->child = x->right; } FibNodeAdd(x, z);//add x to the root list heap x->parent = NULL; } FibNodeRemove(z); if (z->right == z) { heap->min = NULL; } else { heap->min = z->right; FibHeapConsolidate(heap);//合并两个度相同的树 } heap->keyNum--; } return z; } //合并左右相同度数的二项树 void FibHeapConsolidate(FibHeap * heap) { int D, d; FibNode * w = heap->min, * x = NULL, * y = NULL; FibHeapConsMake(heap);//开辟哈希所用空间 D = heap->maxNumOfDegree + 1; for (int i = 0; i < D; i++) { *(heap->cons + i) = NULL; } //合并相同度的根节点,使每个度数的二项树唯一 while (NULL != heap->min) { x = FibHeapMinRemove(heap); d = x->degree; while (NULL != *(heap->cons + d)) { y = *(heap->cons + d); if (x->key > y->key) {//根结点key最小 swap(x, y); } FibHeapLink(heap, y, x); *(heap->cons + d) = NULL; d++; } *(heap->cons + d) = x; } heap->min = NULL;//原有根表清除 //将heap->cons中结点都重新加到根表中,且找出最小根 for (int i = 0; i < D; i++) { if (*(heap->cons + i) != NULL) { if (NULL == heap->min) { heap->min = *(heap->cons + i); } else { FibNodeAdd(*(heap->cons + i), heap->min); if ((*(heap->cons + i))->key < heap->min->key) { heap->min = *(heap->cons + i); }//if(<) }//if-else(==) }//if(!=) }//for(i) } //<辅助函数>将x根结点链接到y根结点 void FibHeapLink(FibHeap * heap, FibNode * x, FibNode *y) { FibNodeRemove(x); if (NULL == y->child) { y->child = x; } else { FibNodeAdd(x, y->child); } x->parent = y; y->degree++; x->marked = false; } //<辅助函数,c++中可以用map代替>开辟FibHeapConsolidate函数哈希所用空间 static void FibHeapConsMake(FibHeap * heap) { int old = heap->maxNumOfDegree; heap->maxNumOfDegree = int(log(heap->keyNum * 1.0) / log(2.0)) + 1; if (old < heap->maxNumOfDegree) { //因为度为heap->maxNumOfDegree可能被合并,所以要maxNumOfDegree + 1 heap->cons = (FibNode **) realloc(heap->cons, sizeof(FibHeap *) * (heap->maxNumOfDegree + 1)); if (NULL == heap->cons) { puts("Out of Space!"); exit(1); } } } //<辅助函数>将堆的最小结点移出,并指向其有兄弟 static FibNode *FibHeapMinRemove(FibHeap * heap) { FibNode *min = heap->min; if (heap->min == min->right) { heap->min = NULL; } else { FibNodeRemove(min); heap->min = min->right; } min->left = min->right = min; return min; } //减小一个关键字 void FibHeapDecrease(FibHeap * heap, FibNode * x, int key) { FibNode * y = x->parent; if (x->key < key) { puts("new key is greater than current key!"); exit(1); } x->key = key; if (NULL != y && x->key < y->key) { //破坏了最小堆性质,需要进行级联剪切操作 FibHeapCut(heap, x, y); FibHeapCascadingCut(heap, y); } if (x->key < heap->min->key) { heap->min = x; } } //切断x与父节点y之间的链接,使x成为一个根 static void FibHeapCut(FibHeap * heap, FibNode * x, FibNode * y) { FibNodeRemove(x); renewDegree(y, x->degree); if (x == x->right) { y->child = NULL; } else { y->child = x->right; } x->parent = NULL; x->left = x->right = x; x->marked = false; FibNodeAdd(x, heap->min); } //级联剪切 static void FibHeapCascadingCut(FibHeap * heap, FibNode * y) { FibNode * z = y->parent; if (NULL != z) { if (y->marked == false) { y->marked = true; } else { FibHeapCut(heap, y, z); FibHeapCascadingCut(heap, z); } } } //修改度数 void renewDegree(FibNode * parent, int degree) { parent->degree -= degree; if (parent-> parent != NULL) { renewDegree(parent->parent, degree); } } //删除结点 void FibHeapDelete(FibHeap * heap, FibNode * x) { FibHeapDecrease(heap, x, INT_MIN); FibHeapExtractMin(heap); } //堆内搜索关键字 FibNode * FibHeapSearch(FibHeap * heap, int key) { return FibNodeSearch(heap->min, key); } //被FibHeapSearch调用 static FibNode * FibNodeSearch(FibNode * x, int key) { FibNode * w = x, * y = NULL; if (x != NULL) { do { if (w->key == key) { y = w; break; } else if (NULL != (y = FibNodeSearch(w->child, key))) { break; } w = w->right; } while (w != x); } return y; } //销毁堆 void FibHeapDestory(FibHeap * heap) { FibNodeDestory(heap->min); free(heap); heap = NULL; } //被FibHeapDestory调用 static void FibNodeDestory(FibNode * x) { FibNode * p = x, *q = NULL; while (p != NULL) { FibNodeDestory(p->child); q = p; if (p -> left == x) { p = NULL; } else { p = p->left; } free(q->right); } } //输出打印堆 static void FibHeapPrint(FibHeap * heap) { printf("The keyNum = %d\n", heap->keyNum); FibNodePrint(heap->min); puts("\n"); }; //被FibHeapPrint调用 static void FibNodePrint(FibNode * x) { FibNode * p = NULL; if (NULL == x) { return ; } p = x; do { printf(" ("); printf("%d", p->key); if (p->child != NULL) { FibNodePrint(p->child); } printf(") "); p = p->left; }while (x != p); } int keys[10] = {1, 2, 3, 4, 5, 6, 7, 9, 10, 11}; int main() { FibHeap * heap = NULL; FibNode * x = NULL; heap = FibHeapMake(); FibHeapInsertKeys(heap, keys, 10); FibHeapPrint(heap); x = FibHeapExtractMin(heap); printf("抽取最小值%d之后:\n", x->key); FibHeapPrint(heap); x = FibHeapSearch(heap, 11); if (NULL != x) { printf("查找%d成功,", x->key); FibHeapDecrease(heap, x, 8); printf("减小到%d后:\n", x->key); FibHeapPrint(heap); } x = FibHeapSearch(heap, 7); if (NULL != x) { printf("删除%d成功:\n", x->key); FibHeapDelete(heap, x); FibHeapPrint(heap); } FibHeapDestory(heap); return 0; }
4.用于不相交集合的数据结构
其实就是并查集,链表形式不再给出,非链表形式,参见树基础总结:http://blog.csdn.net/chinajane163/article/details/49468737