zoukankan      html  css  js  c++  java
  • 排序算法之堆排序

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

      堆的定义: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).
      它是不稳定的排序方法。
  • 相关阅读:
    Vim step by step
    Ubuntu解压命令全览
    这样才能使本地Mysql服务允许被外部主机连接(两步)
    [Python] logging.logger
    Python Selenium
    MySQL中char、varchar和text的区别
    Way to MongoDB
    Python误区之strip,lstrip,rstrip
    Android Studio Tips
    Way to tmux
  • 原文地址:https://www.cnblogs.com/zhuwbox/p/3630037.html
Copyright © 2011-2022 走看看