堆排序
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了堆排序相关的知识,希望对你有一定的参考价值。
N个元素称为堆。若它的元素序列k[1],k[2],k[3].....K[n]满足
k[i]<=k[2i] ,k[i]<=k[2i+1] 1<=i<=n/2
则称之为最小堆(min_heaps), 假设满足
k[i]>=k[2i] ,k[i]>=k[2i+1] 1<=i<=n/2
则称之为最大堆(min_heaps)。
================
以下左边的图表示最大堆。右边表示堆在数组中的存取情况
以下以最大堆为例。说明堆的操作。
一:堆的上移动操作
假设改动堆中某个元素。使得它的值大于父节点的,这就破坏了最大堆的性质。因此须要对堆实现上移操作。
//对下标为i的数进行上移动操作,flag标记改动后的值是否大于父节点 void sift_up(int *H,int i) { bool flag=true; while(flag &&i!=1) { if(H[i]>H[i/2]) swap(H[i],H[i/2]); else flag=false; i=i/2; } }
二:堆的下移操作
假设改动堆中某个元素,使得它的值小于孩子节点。这就破坏了最大堆的性质。因此须要对堆实现下移操作。
//形參m为堆元素个数 void sift_down(int *H,int m,int i) { bool flag=true; while(flag && (i=2*i)<=m) { if(i+1<=m && H[i+1]>H[i]) //选取须要下移的节点的左右孩子节点中较大的那个元素 i++; if(H[i]>H[i/2]) //此时的i/2即为我们须要下移的元素。假设它比它的左右孩子节点中较大的那个要小,那么两者互换 swap(H[i],H[i/2]); else flag=false; } }
三:堆的删除操作
假设我们要删除下标为 i 的元素,那么我们能够用堆中最后1个元素替代i,然后对这个替代后的元素运行上移或者下移操作
//删除堆中下表为i的元素,最后元素个数由m带回 void heap_delete(int *H,int i ,int &m) { if(i>m) cout<<"overflow"<<endl; else { int a,b; a=H[i]; b=H[m]; H[i]=b; m--; if(a>b) { sift_down(H,m,i); } else { sift_up(H,i); } } }四:堆的插入操作
插入到堆最后1个元素后面,然后对该元素运行上移操作
//形參m为堆元素的个数,num为插入的数 void Insert(int *H,int &m,int num) { m++; H[m]=num; sift_up(H,m); }五:堆的建立
把要建立成堆的元素依次调用上面的插入函数,一个一个插入,就成了堆
// A为须要建立堆的数组,H为所建立的堆,g为A中的数组元素个数。m为堆的元素,最后该參数返回堆的元素个数 //堆是从H[1]開始存储元素 void make_heap(int *A,int *H,int g,int &m) { m=0; for(int i=0;i<g;i++) { Insert(H,m,A[i]); } }
六:堆的排序
如果堆有n个元素。因为堆的第一个元素一定是最大的元素,因此能够让第一个元素和第n个元素互换。然后对第一个元素运行下操作。
接着,对n-1个元素运行同样的操作,让第一个元素与第n-1个元素互换,然后对第一个元素运行下移操作。依次类推,最后的堆就是从小到大排序好的堆。
//堆排序,m为堆的元素个数 void heap_sort(int *H,int m) { int i; for(i=m;i>0;i--) { swap(H[i],H[1]); sift_down(H,i-1,1); //注意第2个參数为i-1,不是m,是对接下来的i-1个堆元素进行操作 } }
完整的代码例如以下:
#include<iostream> using namespace std; void swap(int &a,int &b) { int c; c=a; a=b; b=c; } //对下标为i的数进行上移动操作 void sift_up(int *H,int i) { bool flag=true; while(flag &&i!=1) { if(H[i]>H[i/2]) swap(H[i],H[i/2]); else flag=false; i=i/2; } } //形參m为堆元素个数 void sift_down(int *H,int m,int i) { bool flag=true; while(flag && (i=2*i)<=m) { if(i+1<=m && H[i+1]>H[i]) //选取须要下移的节点的左右孩子节点中较大的那个元素 i++; if(H[i]>H[i/2]) //此时的i/2即为我们须要下移的元素。假设它比它的左右孩子节点中较大的那个要小,那么两者互换 swap(H[i],H[i/2]); else flag=false; } } //形參m为堆元素的个数,num为插入的数 void Insert(int *H,int &m,int num) { m++; H[m]=num; sift_up(H,m); } // A为须要建立堆的数组。H为所建立的堆,g为A中的数组元素个数。m为堆的元素。最后该參数返回堆的元素个数 //堆是从H[1]開始存储元素 void make_heap(int *A,int *H,int g,int &m) { m=0; for(int i=0;i<g;i++) { Insert(H,m,A[i]); } } //删除堆中下表为i的元素,最后元素个数由m带回 void heap_delete(int *H,int i ,int &m) { if(i>m) cout<<"overflow"<<endl; else { int a,b; a=H[i]; b=H[m]; H[i]=b; m--; if(a>b) { sift_down(H,m,i); } else { sift_up(H,i); } } } //堆排序,m为堆的元素个数 void heap_sort(int *H,int m) { int i; for(i=m;i>0;i--) { swap(H[i],H[1]); sift_down(H,i-1,1); //注意第2个參数为i-1。不是m。是对接下来的i-1个堆元素进行操作 } } void main() { int A[5]={34,11,242,22,4}; int H[6]; int m,i; make_heap(A,H,5,m); for(i=1;i<=m;i++) { cout<<H[i]<<" "; } cout<<endl; cout<<"上移操作:"<<endl; H[5]=999; sift_up(H,5); for(i=1;i<=m;i++) { cout<<H[i]<<" "; } cout<<endl; cout<<"下移操作:"<<endl; H[1]=1; sift_down(H,m,1); for(i=1;i<=m;i++) { cout<<H[i]<<" "; } cout<<endl; cout<<"元素删除操作,删除堆顶元素"<<endl; heap_delete(H,1,m); for(i=1;i<=m;i++) { cout<<H[i]<<" "; } cout<<endl; cout<<"堆排序:"<<endl; heap_sort(H,m); for(i=1;i<=m;i++) { cout<<H[i]<<" "; } cout<<endl; system("pause"); }
时间复杂度为O(nlogn)
以上是关于堆排序的主要内容,如果未能解决你的问题,请参考以下文章