堆排序

堆排序:

要知道堆排序,首先要了解一下二叉树的模型。

下图就是一颗二叉树,具体的情况我后续会分享的。

那么堆排序中有两种情况(看上图理解):

    大根堆:  就是说父节点要比左右孩子都要大。

    小根堆:  就是说父节点要比左右孩子都要小。

那么要实现堆排序,必须要做两件事情:

   第一:构建大根堆。

           首先上图:

           

首先这是一个无序的堆,那么我们怎样才能构建大根堆呢?

     第一步: 首先我们发现,这个堆中有2个父节点(2,,3);

     第二步: 比较2这个父节点的两个孩子(4,5),发现5大。

     第三步: 然后将较大的右孩子(5)跟父节点(2)进行交换,至此3的左孩子堆构建完毕,

                 如图:

                         

     第四步: 比较第二个父节点(3)下面的左右孩子(5,1),发现左孩子5大。

     第五步: 然后父节点(3)与左孩子(5)进行交换,注意,交换后,堆可能会遭到破坏,

                 必须按照以上的步骤一,步骤二,步骤三进行重新构造堆。

           

最后构造的堆如下:

                 

   第二:输出大根堆。

             至此,我们把大根堆构造出来了,那怎么输出呢?我们做大根堆的目的就是要找出最大值,

         那么我们将堆顶(5)与堆尾(2)进行交换,然后将(5)剔除根堆,由于堆顶现在是(2),

         所以破坏了根堆,必须重新构造,构造完之后又会出现最大值,再次交换和剔除,最后也就是我们

         要的效果了。

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

ki<=k(2i)且ki<=k(2i+1)(1≤i≤ n/2),当然,这是小根堆,大根堆则换成>=号。//k(i)相当于二叉树的非叶子结点,K(2i)则是左子节点,k(2i+1)是右子节点
 
      堆排序利用了大根堆(或小根堆)堆顶记录的关键字最大(或最小)这一特征,使得在当前无序区中选取最大(或最小)关键字的记录变得简单。

说明:它是不稳定的排序方法。(排序的稳定性是指如果在排序的序列中,存在前后相同的两个元素的话,排序前 和排序后他们的相对位置不发生变化)

 1 #include<iostream>
 2 #include <ctime>  //使用了time() 函数 
 3 #include <cstdlib>    //使用了srand()函数 
 4 
 5 const int N=3005;
 6 int a[N];
 7 using namespace std;
 8 //堆排序
 9 //整理节点time:O(lgn)
10 
11 void AdjustHeap(int *a,int parent,int length)
12 {
13      //temp保存当前父节点 
14     int temp=a[parent];
15     //得到左孩子(这可是二叉树的定义,大家看图也可知道)
16     int child=2*parent;
17     while(child<=length)
18     {
19         //如果parent有右孩子,则要判断左孩子是否小于右孩子
20         if (child + 1 < length && a[child] < a[child + 1])   child++;//此方法用了点小技巧,大家认真思考一下 
21         if(temp>=a[child])  break;    //父节点大于子节点,就不用了,建堆完成 
22         //另一种情况则是小于
23         a[parent]=a[child];  //大的子节点赋值给父节点 
24         parent=child;     //子节点做为父节点 
25         child=2*parent;   //步长增,进入下一节点 
26     }
27     a[parent]=temp;   //完成后,将temp赋值给此节点即可完成建堆 
28 }
29 
30 //堆排序time:O(nlgn)
31 void HeapSort(int*a,int n)
32 {
33   int i;
34   for(i=n/2;i>=1;i--)     //从最后一个父节点一至到第一个 
35   {
36     AdjustHeap(a,i,n);     //对每个父节点进行一次建堆 
37   }
38   //最后输出堆元素
39  for(int i=n;i>1;i--)
40  {
41      //堆顶与当前的第i个元素进行对调
42      int temp=a[1];
43      a[1]=a[i];
44      a[i]=temp;
45      //对调后,再对1...i-1的元素进行建堆,此时只需要从第1个元素开始到最底层即可
46       AdjustHeap(a,1,i-1); 
47  }
48 }
49 
50 int main()
51 {
52     //随机产生3000个数存入数组a中 
53     srand(int(time(0)));      //利用时间函数time(),产生每次不同的随机数种子
54     for(int i=1;i<=3000;i++) a[i]=rand();  //随机产生3000个数存于数组a中 (从1开始) 
55     clock_t start = clock();     // 计时开始
56     HeapSort(a, 3000);                   //对数组a进行排序(从1开始) 
57     clock_t end = clock();       //计时结束
58     for(int i=1;i<=20;i++) cout<<a[i]<<' ';    //输出前20个数据(已从小到大排序) 
59     cout<<endl<<"堆排排序耗时为:"<<end-start<<"ms"<<endl;     //输出排序所用的时间:结束时间-开始时间(单位为毫秒)
60     return 0;
61 }
原文地址:https://www.cnblogs.com/jjzzx/p/5072111.html