前言:今天我来介绍下堆排序,在写堆排序代码之前,我们要知道堆的概念!

  堆的定义:n个关键字序列Kl,K2,…,Kn称为(Heap),当且仅当该序列满足如下性质(简称为堆性质):

  (1)ki<=k(2i)且ki<=k(2i+1)(1≤i≤ n),当然,这是小根堆,大根堆则换成>=号。//k(i)相当于二叉树的非叶子结点,K(2i)则是左子节点,k(2i+1)是右子节点
若将此序列所存储的向量R[1..n]看做是一棵完全二叉树的存储结构,则堆实质上是满足如下性质的完全二叉树:
  树中任一非叶子结点的关键字均不大于(或不小于)其左右孩子(若存在)结点的关键字。
  堆的分类:
    1).大根堆和小根堆:根结点(亦称为堆顶)的关键字是堆里所有结点关键字中最小者的堆称为小根堆,又称最小堆。
    2).根结点(亦称为堆顶)的关键字是堆里所有结点关键字中最大者,称为大根堆,又称最大堆。
    注意:①堆中任一子树亦是堆。②以上讨论的堆实际上是二叉堆(Binary Heap),类似地可定义k
  堆排序的思想:   
    1).先将初始文件R[1..n]建成一个大根堆,此堆为初始的无序区
    2).再将关键字最大的记录R[1](即堆顶)和无序区的最后一个记录R[n]交换,由此得到新的无序区R[1..n-1]和有序区R[n],且满足R[1..n-1].keys≤R[n].key
    3).由于交换后新的根R[1]可能违反堆性质,故应将当前无序区R[1..n-1]调整为堆。然后再次将R[1..n-1]中关键字最大的记录R[1]和该区间的最后一个记录R[n-1]交换,由此得到新的无序区R[1..n-2]和有序区R[n-1..n],且仍满足关系R[1..n-2].keys≤R[n-1..n].keys,同样要将R[1..n-2]调整为堆。
  堆排序的参考代码:
  以下代码参考算法导论和百度百科!
 1 //堆排序,伪代码
 2 // HEAPSORT(A)
 3 // BUILD-MAX-HEAP(A)
 4 // for i=A.length downto 2
 5 //       exchange A[1] with A[i]    //将第一个元素和第i个元素交换
 6 // A.heap-size = A.heap-size - 1;
 7 // MAX-HEP-APIFY(A,1);
 8 
 9 void heapAdjust(int iarr[],int start,int len)
10 {
11     int i = start,temp = 0,maxChildIndex = 0;
12     for(;i < len / 2; ++i)
13     {
14         maxChildIndex = 2*i+1;
15         if(maxChildIndex + 1 < len)    //如果右节点存在
16         {
17             maxChildIndex = iarr[maxChildIndex] > iarr[maxChildIndex + 1] ? maxChildIndex: maxChildIndex + 1;
18         }
19         if(iarr[i] < iarr[maxChildIndex])
20         {
21             temp = iarr[maxChildIndex];
22             iarr[maxChildIndex] = iarr[i];
23             iarr[i] = temp;
24         }
25     }
26 }
27 void heapSort(int iarr[],int len)
28 {
29     int i,temp;
30     //开始的时候建堆,根据堆的性质,从原始数组的最后一个元素的父节点开始调整即可
31     for(i = len / 2; i > 0; --i)
32     {
33         heapAdjust(iarr,i - 1,len);
34     }
35     for(i = 0;i < len; ++i)
36     {
37         cout << iarr[i] << " ";
38     }
39     cout << endl;
40     for(i = len - 1; i > 0; --i)
41     {
42         //堆的最后一个元素与第一个元素交换
43         temp    = iarr[i];
44         iarr[i] = iarr[0];    
45         iarr[0] = temp;
46         heapAdjust(iarr,0,i);
47     }
48 }
49 int main()
50 {
51     int a[10]={4,1,3,2,16,9,10,14,8,7};
52     heapSort(a,10);
53     for(int i=0;i < 10; ++i)
54         cout << a[i] << " ";
55     return 0;
56 }
算法分析:
  堆排序的时间,主要由建立初始堆和反复重建堆这两部分的时间开销构成,它们均是通过调用heapAdjust实现的。
  平均性能:O(N*logN) = O(N)(heapSort中的两个for循环的时间复杂度)*O(lonN)(heapAdjust中的for循环的时间复杂度)。
  由于建初始堆所需的比较次数较多,所以堆排序不适宜于记录数较少的文件。
  堆排序是就地排序,辅助空间为O(1).
  它是不稳定的排序方法。
posted on 2014-03-28 10:02  水目沾  阅读(597)  评论(1编辑  收藏  举报