经典算法回顾之堆排序【选择排序】

堆排序与快速排序、归并排序都是时间复杂度为O(NlogN)的常见排序算法。堆排序是一种利用堆的性质进行排序的算法。


最大堆(最小堆与之类似)是一种比较特殊的完全二叉树,它满足两个特性:
  1. 父节点的值总是大于等于任何一个子节点的值。
  2. 每个节点的左子树和右子树都是一个最大堆。
  【若根的下标从1开始,则节点i的左孩子下标为2i,右孩子下标为2i+1】


堆排序过程为:
  1. 建立最大堆
  2. 将堆顶元素与最后一个元素交换,将交换后的序列调整为新堆,重复该过程,知道有序元素为n-1个


故,堆排序的基本框架为

1 void HeapSort(int a[], int size)
2 {
3     BuildHeap(a, size);
4     for(int i = size; i >= 2; i--)
5     {
6         swap(a[1], a[i]);//交换堆顶和最后一个元素,即每次将剩余元素中的最大者放到最后面
7         HeadAdjust(a ,1 ,i-1);//重新调整,将余下元素调成大顶堆
8     }
9 }        

 
完整代码如下:

  1 #include "stdafx.h"
  2 #include <iostream>
  3 using namespace std;
  4 
  5 /*堆排序
  6  *时间复杂度:O(NlogN)
  7  *堆是完全二叉树;且有大小堆之分
  8  *注意:数组从1开始,因为这样才满足;对于节点i,其左孩子是2*i,右孩子是2*i+1
  9  *应用:最大堆排序用来从小到大排序数列
 10  *扩展:堆经常被用来求一个数列中的第K大的数,只需要建立一个大小为K的最小堆,堆顶就是第K大的数;
 11  *        (如:选前k个数建最小堆,若后面的数比堆顶小,舍弃;若比堆顶大,舍弃堆顶,将数据插入
 12  *            调整最小堆)!!!想清楚
 13  *            求数列中第K小的数,建立大小为K的最大堆,堆顶就是第K小的数。复杂度为O(NlogK)
 14  *            //参见《编程之美》Chapter5的第五节
 15 */
 16 
 17 #include "stdafx.h"
 18 #include <algorithm>//std::swap()
 19 #include <iostream>
 20 
 21 using namespace std;
 22 
 23 void HeapAdjust(int a[], int i, int size)
 24 //a为堆数组,i为当前要调整的节点,以下标i为堆顶,将剩余size个元素调整为堆(即节点数为size)
 25  
 26 {
 27        int lchild = 2 * i;//i节点左右孩子节点序号
 28        int rchild = 2 * i + 1;
 29        int max = i;//临时变量,记录最大值的下标
 30  
 31        if(i <= size / 2)//对叶节点i进行调整
 32        {
 33               if(lchild <= size && a[lchild] > a[max])//注意边界检查,包括size
 34                      max = lchild;
 35               if(rchild <= size && a[rchild] > a[max])
 36                      max = rchild;
 37               if(max != i)//需调整
 38               {
 39                      swap(a[i], a[max]);
 40                      HeapAdjust(a, max, size); //避免调整后以下标max为父节点的子树不是堆
 41               }
 42        }
 43 }
 44 
 45 /*
 46 void HeapAdjust(int a[], int i, int size)
 47 //堆调整非递归实现
 48 {
 49     a[0] = a[i];//用空闲位置a[0]临时记录当前节点的值
 50     int lchild = 2 * i;
 51     int max = i;//临时变量,记录最大值的下标
 52 
 53     while( lchild <= size )
 54     {
 55         if(lchild <= size && a[lchild] > a[max])//注意边界检查,包括size
 56             max = lchild;
 57         if((lchild + 1 <= size) && a[lchild+1] > a[max])
 58             max = lchild+1;
 59         if(max == i)
 60             break;//无需调整
 61         else
 62         {
 63             //a[i] = a[max];//!!
 64             swap(a[i],a[max]);
 65             i = max;//!!
 66             lchild = i * 2; //!!
 67         }        
 68     }
 69     //a[i] = a[0];//!!被调整节点的值被放入最终位置
 70 }

 71 */
 72 
 73 
 74 void BuildHeap(int a[], int size)//建堆
 75 {
 76     for(int i = size/2; i >= 1; --i)//非叶节点最大序号值为size/2 
 77         HeapAdjust(a, i, size);
 78 }
 79 
 80 void HeapSort(int a[],int size)//堆排序
 81 {
 82     BuildHeap(a,size);
 83     for(int i=size;i>=2;--i)//到2!
 84     {
 85         swap(a[1],a[i]);//交换堆顶和最后一个元素,即每次将剩余元素中的最大者放到最后面
 86         HeapAdjust(a,1,i-1);//重新调整,将余下元素调成大顶堆
 87     }
 88 }
 89 
 90 int main(int argc, char *argv[])
 91 {
 92     //int a[] = {0,16,20,3,11,17,8};
 93     int a[100];
 94     cout << "请输入要排序的数字个数:" ;
 95     int size;
 96     while(cin >> size && size>0 && size < 100)
 97     {
 98         for(int i=1; i <= size; ++i)//注意从下标1开始存数据
 99             cin >> a[i];
100         HeapSort(a,size);
101         for(int i=1; i <= size; ++i)
102             cout << a[i] << " ";
103         cout << endl;
104         cout << "请输入要排序的数字个数:" ;
105     }
106     return 0;
107 }
View Code

参考:http://www.cnblogs.com/dolphin0520/archive/2011/10/06/2199741.html

  

原文地址:https://www.cnblogs.com/dreamrun/p/4365387.html