堆排序
堆排序是我们熟知的常用的排序算法。
首先先介绍一下什么是堆排序。堆指的并不是我们数据结构上分配内存的堆栈,在这里指的是大顶堆和小顶堆。大顶堆是指根节点数值大于左右孩子节点的完全二叉树,也有可能是满二叉树。总之,就是一颗存贮数值的树。我们可以利用根节点大于孩子节点这一特性对一组数组建立堆,不断取出根节点再建立大顶堆的过程来完成对数组排序。
对于大顶堆,可以用这样的数学角度考虑,即a[i]>a[2i],且a[i]>a[2i+1],也就是上面所说的根节点的数值大于左右孩子节点的数值。对于小顶堆则反之。
知道了大顶堆的概念,如何建立大顶堆呢?可以这样考虑。大顶堆父亲节点肯定大于孩子节点,所以首先对一个节点与其左右孩子节点比较,选取数值最大的那个,交换数值变成大顶堆。如果过程中交换了数值,说明被交换的那个孩子还需要再对其考虑一次是否为大顶堆,再对它进行一次比较。如此一来,我们完全可以利用递归调用自身的方式进行建堆。
上面的第一步只是保证那一部分是大顶堆,如何保证整棵树都是大顶堆的形式?这就需要我们把每一个非叶子节点比较一次。但是按照什么样的顺序呢?为了保证整个树为大顶堆,这就需要我们从下而上建立大顶堆。请你自己思考一下这是为什么。也就是从n/2(n指的是总长度)到1逐个比较排序建堆。当然,对于叶子节点根本无需考虑,因为总之它的父母会主动和他们比较。
以上两步工作是完成建立大顶堆,可是我们的堆排序呢?接下来,我们就是巧妙的利用大顶堆的性质,对数组进行交换排序。具体操作为:首先交换a[0]和a[n],即交换最大的到最后。然后再把剩余的1到n-1的堆重新进行堆排序。反复循环。这样就可以保证堆的第一个元素永远是最大的。不断交换第一个元素与最后一个,完成从小到大排序。
十分简单而又十分优雅的排序,但是我习惯上用0作为数组的第一个下标,所以各种下标处理就需要格外谨慎。左右孩子节点处理也要十分小心。已下为具体的C++程序代码,CB已经完美运行。
1 #include<iostream> 2 using namespace std; 3 4 int a[10] = {4,1,3,2,16,9,10,14,8,7}; 5 int len = sizeof(a)/sizeof(int); 6 7 inline int PARENT(int i){ 8 return i/2; 9 } 10 11 inline int LEFT(int i){ 12 return 2*i+1; 13 } 14 15 inline int RIGHT(int i){ 16 return 2*i+2; 17 } 18 19 void exchange(int &a, int &b){ 20 int temp = a; 21 a = b; 22 b = temp; 23 } 24 25 void maxHeapify(int a[], int i){ //put the index i into the right place 26 int maxIndex; 27 if(LEFT(i) < len && a[LEFT(i)] > a[i]){ 28 maxIndex = LEFT(i); 29 }else{ 30 maxIndex = i; 31 } 32 if(RIGHT(i) < len && a[RIGHT(i)] > a[maxIndex]){ 33 maxIndex = RIGHT(i); 34 } 35 36 if(maxIndex != i){ 37 exchange(a[i], a[maxIndex]); 38 maxHeapify(a, maxIndex); 39 } 40 } 41 42 void buildMaxHeap(int a[]){ 43 int l; 44 if(len%2 == 0) 45 l = len/2 - 1; 46 else 47 l = len/2; 48 for(int i = l; i >= 0; --i) 49 maxHeapify(a, i); 50 } 51 52 void heapSort(int a[]) { 53 buildMaxHeap(a); 54 for(int i = len - 1; i != 0; --i){ 55 exchange(a[0], a[i]); 56 --len; 57 buildMaxHeap(a); 58 } 59 } 60 int main() { 61 buildMaxHeap(a); 62 for(int i = 0; i < 10; ++i) 63 cout<<a[i]<<" "; 64 cout<<endl; 65 heapSort(a); 66 for(int i = 0; i < 10; ++i) 67 cout<<a[i]<<" "; 68 69 return 0; 70 }