堆和堆排序
-
前提:完全二叉树
-
树T中的任一结点的值不小于它的左子结点的值(如果左子结点存在),且不小于它的右子结点的值(如果右子结点存在),那么我们称树T是一个堆(heap)
-
分为大根堆和小根堆
-
存放:按照层次序存放
-
(1)若2i+1<n,则 a[i]≥a[2i+1] (2)若2i+2<n,则 a[i]≥a[2i+2]
-
步骤
-
建初堆,将a[0]…a[n-1]建成为一个堆
-
i 从n-1到1(共进行n-1次)重复进行:
a)交换a[0]与a[i],
b)将a[0]到a[i-1]之间的结点调整成堆。
重复执行,直到调整范围内只有一个结点a[0]为止。
-
-
调整堆
-
自上而下siftdown,根结点的左右子树都是堆,根结点不满足堆的特性
void siftdown(int a[],int i,int n){ //调整以a[i]为根的二叉树,使其成为堆。前提条件:树中各子树都是堆。 int j,t; t=a[i]; while((j=2*i+1)<n){//当有子结点的时候才调整 //让j指向a[i]左子结点和右子结点(如果有)中键值较大的一个 if(j<n-1&&a[j]<a[j+1])j++; if(t<a[j])//如果t比子结点小,不符合堆的定义 { a[i]=a[j];//将较大的值放入根a[i]。 i=j;// 结点a[j]作为新的将要调整的子树的根,“交换”,省略了a[j]=t;的操作 } else break; } a[i]=t;//循环终止后,t填入当前停止的结点 }
-
自下而上swim,除了新加入的叶子结点,其他结点已经是堆。
“上浮”调整。 用途:已建立好堆,如果有新的元素插入,则放在最后(作为最
后一个叶子结点),进行上浮调整。void swin(int a[],int k){ //由于a[k]比它的父结点大,破坏了堆,通过减缓它和它的父结点来修复 while(k>0&&a[k-1]/2<a[k]){ swap(a[(k-1)/2],a[k]); k=(k-1)/2; } }
-
-
建堆
void buildheap(int a[],int n){ //从最后一个非叶子结点开始调整,依次向上,一直到根a[0],构成初始堆 for(int i=(n-2)/2;i>=0;i--){ siftdown(a,i,n); } }
复杂度O(nlog(n))
-
堆排序
void heap_sort(int a[],int n){ //1、建立初始堆 //2、调整堆 int i,m,t; for(i=(n-2)/2;i>=0;i--)siftdown(a,i,n);//从最 后一个非叶子结点开始逐渐调整,向上一直到根a[0],构成初始堆,即建堆 for(m=n-1;m>0;m--){ swap(a[0],a[m]); siftddown(a,0,m);调整以a[0]为根的二叉树,调整涉及 的结点有m个,m在逐渐减小。 } }
-
执行分析
siftdown(a,i,n):循环的执行次数与表示堆的二叉树的层数有关。
有n个结点的完全二叉树的层数为调用一次的时间复杂度O(log2n)
建初堆,循环最多执行n/2次,时间复杂度O(nlog2n)
调整,所以整个执行n-1次,时间复杂度O(nlog2n)
所以整个堆排序的时间复杂度O(nlog2n)
空间复杂度:O(1) -
算法特点
(1)堆排序是不稳定的。
(2)不能用于链表。
(3)初始建堆需要的比较次数多。最坏情况下时间复杂度O(nlog2n),好于其他排序 (最坏情况O(n2));
-
堆的应用——优先队列
任务调度。根据任务到达先后排队,但优先级高的先被执行
void insert(priorityq * h,elem x){//swin int k; if(isfull(h)){cout<<"priority queue is full"<<endl;return;} h->elem[h->size++]=x; k=h->size-1; while(k>0&&h->elem[(k-1)/2]<h->elem[k]){ swap(h->elem[k],h->elem[(k-1)/2]); k=(k-1)/2; } } elem delete(priorityq*h){//s int i,child; elem max,last; if(isempty(h)){cout<<"priority queue is empty.";return -1;} max=h->elem[0]; last=h->elem[h->size-1]; h->size--; i=0; while((child=2*i+1)<h->size)//当i有子结点的时候才需要调整 { if(child<h->size-1&&h->elem[child]<h->elem[child+1])child++; if(last<h->elem[child]){ h->elem[i]=h->elem[child]; i=child; } else break; } h->elem[i]=last; return max; }