首页 > 代码库 > 排序算法之堆排序

排序算法之堆排序

个人感觉堆排序还是在排序算法中比较难懂的,看了一段时间。准备把其中的思路理一理。

首先,堆分为大根堆和小根堆。

堆是满足下列性质的数列{r1, r2, …,rn}:

  即任何一非叶节点的关键字不大于或者不小于其左右孩子节点的关键字。

那么如何进行排序呢?

  1. 我们要把序列构建为堆,建堆的核心就是不断的调整堆,是非叶子节点的值大于或小于孩子节点。这样,我们得到的初始堆,其根节点的值必定是最大值或最小值。

      2. 然后我们将根节点与当前堆的最后一个节点进行交换,这样最后一个节点必定是最大或最小值。将最后一个节点(即最大或最小值)输出,通常将堆底元素送入堆顶,此时根节点已不满足堆的性质,我们需要将剩余的这个堆进行调整使之成为大根堆或小根堆。也就是说,每次我们取得都是根节点的值。取完根节点,再调整堆。

     3. 重复 步骤2,直到堆中仅剩下一个元素为止.(也就是堆的长度为1时)

所以,函数调整堆adjustHeap()是关键。

代码如下:

 1 #include<iostream> 2 using namespace std; 3  4 void adjustHeap(int A[], int len,int k) 5 { 6     int lchild = k*2+1,rchild = lchild+1; 7      8     while(rchild<len) 9     {10         if(A[k]>=A[lchild]&&A[k]>=A[rchild])11         {12             return;13         }14         //取出左右节点的最大值并与其父节点交换15         if (A[lchild]>A[rchild])16         {17             swap(A[k],A[lchild]);18             k = lchild;19         }20         else21         {22             swap(A[k],A[rchild]);23             k = rchild;24         }25         //堆lchild和rchild重新赋值,循环26         lchild = k*2+1;27         rchild = lchild+1;28     }29     //如果只有左孩子,比较左孩子节点与父节点的值30     if(lchild<len&&A[lchild]>A[k])31     {32         swap(A[lchild],A[k]);33         return;34     }35 }36 37 void BuildMaxHeap(int A[],int len)38 {39     for(int i=len/2-1;i>=0;i--)40     {41         adjustHeap(A,len,i);42     }43     for(int i=0;i<8;i++)44     {45         cout<<"init A"<<A[i]<<endl;46     }47 }48 49 void HeapSort(int A[],int len)50 {51     BuildMaxHeap(A,len);52     for(int i=len;i>1;i--)53     {54         //将堆顶元素与堆底元素交换55         swap(A[i-1],A[0]);56         adjustHeap(A,i-1,0);57 58 59     }60 }61 void main()62 {63     int A[8] = {21,45,67,23,76,42,12,54};64     HeapSort(A,8);65     for(int i=0;i<8;i++)66     {67         cout<<A[i]<<endl;68     }69 }

 

算法分析:

平均时间复杂度:堆排序的时间,主要由建立初始堆和反复重建堆这两部分的时间开销构成 。O(nlog2n) 关于性能的计算可见这儿。

空间复杂度:O(1)  (仅用了单数个辅助单元)

稳定性:不稳定。在进行筛选时,有可能把后面相同关键字的元素调到前面,不适合记录较少的排序。

排序算法之堆排序