zoukankan      html  css  js  c++  java
  • 算法学习记录-排序——堆排序

    堆排序

    问题提出:

      前面写过 简单选择排序 讲过,要找出n个数据中最小的值,那么需要比较n-1次。仔细想想,比较第一趟的时候,第一个哨兵下标元素

    与每一个元素比较,如果哨兵元素不是最小的,那么会发生交换,记交换后的元素下标为swap。继续比较第二趟 到 第n趟时候,每一趟都

    会有元素与swap下标元素比较,但是这个比较之前就已经比较了,只是没有保存下来。能不能通过方法能够记录这些比较,使其重复比较的

    次数减少?

      堆排序就是对简单选择排序的一种改进,通过一种数据结构来保存比较之后的元素关系,在大量数据比较时候,效率非常高。

     


     

    堆具有的性质

    n个元素序列{k1,k2,k3,...,kn}满足如下关系:

    • ki ≤ k2i 且 ki ≤ k2i+1     此为:小顶堆

    或者 

    • ki ≥ k2i 且 ki ≥ k2i+1     此为:大顶堆

        (i=1,2,。。。,【n/2】取不大于该值的整数)

              

      若将和此序列对应的一维数组(以一维数组存储)看成是一个完全二叉树,则堆的含义表明,

    完全二叉树中所有非终端结点的值均不大于(或不小于)其左、右孩子的结点值。

    可以推出:堆顶元素一定是序列中最大(最小)的值。

     


     

    堆如何排序?

    以升序排列和大顶堆为例,

    (1)根据堆的性质,堆中最大的元素在堆顶。那么将无序堆中的堆顶元素与堆的最后一个元素交换,原先无序堆中的堆顶交换到无序集合最后一个位置上,该元素归为有序集合。

      (无序堆少一个元素,有序集合多一个元素)

    (2)交换元素后,无序新堆(比之前少一个元素)重新按堆的性质构建新堆。调整完成后,其最大的元素就排在了新堆的堆顶了。

    (3)重复(1)(2)。直到未排序的堆中没有元素。最后产生有序集合。 

     

     其基本思想为(大顶堆):

        1)将初始待排序关键字序列(R1,R2....Rn)构建成大顶堆,此堆为初始的无须区;

        2)将堆顶元素R[1]与最后一个元素R[n]交换,此时得到新的无序区(R1,R2,......Rn-1)和新的有序区(Rn),且满足R[1,2...n-1]<=R[n]; 

        3)由于交换后新的堆顶R[1]可能违反堆的性质,因此需要对当前无序区(R1,R2,......Rn-1)调整为新堆,然后再次将R[1]与无序区最 后一个元素交换,得到新的无序区(R1,R2....Rn-2)和新的有序区(Rn-1,Rn)。不断重复此过程直到有序区的元素个数为n-1,则整个排 序过程完成。


     

    如何构建堆:

      为了能够减少选择排序中重复比较次数,就需要构建一个这样的堆,堆的结构就能够决定堆中元素大小关系,

    省去了反复比较的次数。

    • 所以构建堆是一个重要的过程。(a)
    • 移除堆顶后,重新构造新堆也是一个重要的操作。(b)

    所以 ab 是堆排序的基本操作。

      多思考一下,可以看到a是构建堆的过程,其实质是将无序的堆调整为满足堆性质的过程,与b过程相同。所以ab可以合并为同一个过程。

     

    构建堆(调整堆):

     

    原始图像

     

     

    一维数组是真实的存储结构

    完全二叉树结构则是我们构想的结构。

     

    • 调整堆过程

     

     

    最后我们得到调整好的堆叫初始化堆。之后我们基于这个基础来调整

     

    • 堆排序过程:

     

     

     三条图像分别显示了最大三个数出堆过程。后面过程以此类推。

    基本步骤就是:1.交换——2.入有序队列——3.调整新堆

     

    代码:

     

      1 #include "stdafx.h"
      2 
      3 
      4 typedef int myDataType;
      5 myDataType src_ary[10] = {9,1,5,8,3,7,6,0,2,4};
      6 
      7 void prt_ary(myDataType *ary,int len)
      8 {
      9     int i=0;
     10     while(i < len)
     11     {
     12         printf(" %d ",ary[i++]);
     13     }
     14     printf("
    ");
     15 }
     16 
     17 void prev_process_ary(myDataType *ary,int len)
     18 {
     19     
     20     int i;
     21     len = len+1;
     22     for (i=len-1;i>=0;i--)
     23     {
     24         ary[i] = ary[i-1];
     25     }
     26     ary[0]=0;
     27 }
     28 void retn_process_ary(myDataType *ary,int len)
     29 {
     30     int i;
     31     for (i=0;i<len;i++)
     32     {
     33         ary[i] = ary[i+1];
     34     }
     35 }
     36 
     37 //堆调整——对于堆的调整,我们需要知道参数 : 
     38 //1.调整子树的根结点
     39 //2.树的最后一个叶子节点
     40 void HeapAdjust(myDataType *ary,int Idx,int eIdx)
     41 {
     42     int lchdIdx = 2*Idx;        //左孩子结点的序号
     43     int rchdIdx = lchdIdx+1;    //右孩子结点的序号
     44     
     45 
     46     int i;//i是指向左或者右孩子的序号
     47 
     48     int crntVal = ary[Idx];
     49 
     50     //从将要调整点的孩子孩子节点开始,每次沿着被调整的孩子子树调整
     51     for (i = lchdIdx;i <= eIdx ; i = i*2 )
     52     {
     53         //判断 当前指向点 的左孩子和右孩子大小,取值大的
     54         if (i < eIdx && ary[lchdIdx] < ary[rchdIdx])
     55         {
     56             i=rchdIdx;//如果是右孩子大,则孩子下标为右孩子
     57         }
     58         //如果要调整的子树的根结点大于左右孩子,则不用调整
     59         if (crntVal > ary[i])
     60         {
     61             break;
     62         }
     63 
     64         ary[Idx] = ary[i];
     65 
     66         Idx = i;
     67         lchdIdx = 2*Idx;
     68         rchdIdx = lchdIdx+1;
     69     }
     70     //Idx 为 最后调整到的结点序号,把调整的值 调整到这个序号结点
     71     ary[Idx] = crntVal;
     72 }
     73 
     74 void HeapSort(myDataType *ary,int len)
     75 {
     76     int i;
     77     int endIdx = len+1;
     78     for (i=1 ; i < endIdx ; i++)
     79     {
     80         int temp = ary[1];
     81         ary[1] = ary[endIdx-i];
     82         ary[endIdx-i] = temp;
     83 
     84         HeapAdjust(ary,1,endIdx-i-1);
     85     }
     86 }
     87 
     88 void heapSort(myDataType *ary,int len)
     89 {
     90     int i;
     91 //    printf("prev_process_array:
    ");
     92     prev_process_ary(ary,len);//调整数组,全部右移。这里做实验用,如果是对于很多数组,这样的操作很浪费时间,
     93                                 //就要对HeapSort的序号改进下。避免这样的移位。
     94 //    prt_ary(ary,len+1);
     95 
     96     //建立一个大顶堆
     97     for (i= len/2;i>0;i--)
     98     {
     99         HeapAdjust(ary,i,len);
    100     }
    101     //真正的堆排序
    102     HeapSort(ary,len);
    103 
    104 //    printf("retn_process_array
    ");
    105     retn_process_ary(ary,len);
    106 //    prt_ary(ary,len);
    107 
    108 }
    109 
    110 int _tmain(int argc, _TCHAR* argv[])
    111 {
    112     printf("before sort:
    ");
    113     prt_ary(src_ary,10);
    114 
    115     heapSort(src_ary,10);
    116     
    117     printf("after sort:
    ");
    118     prt_ary(src_ary,10);
    119 
    120 
    121 
    122     getchar();
    123     return 0;
    124 }


    测试结果:

    补充:对于调整还可以使用 递归的方法,可能更容易理解。

    每次对一个点进行调整时候,如果它比孩子小,则和孩子交换,这个时候我们会出现新的子树(以交换的孩子为根),

    继续对这样的子树进行新的调整。这就构成了一个递归调用。

    把之前的HeapAdjust函数修改一下就可以了

     1 void HeapAdjust_R(myDataType *ary,int Idx,int eIdx)
     2 {
     3     int lchdIdx = 2*Idx;        //左孩子结点的序号
     4     int rchdIdx = lchdIdx+1;    //右孩子结点的序号
     5 
     6     if (lchdIdx > eIdx)
     7     {
     8         return ;
     9     }
    10     int crntVal = ary[Idx];
    11 
    12     int i = lchdIdx;//i是指向左或者右孩子的序号
    13 
    14     if (rchdIdx <= eIdx && ary[rchdIdx] > ary[lchdIdx])
    15     {
    16         i = rchdIdx;
    17     }
    18     if (crntVal > ary[i])
    19     {
    20         return ;
    21     }
    22     ary[Idx] = ary[i];
    23     ary[i] = crntVal;
    24     HeapAdjust_R(ary,i,eIdx); 
    25 }

     

  • 相关阅读:
    从有序的数组中查找某个值
    OJ 21658::Monthly Expense(二分搜索+最小化最大值)
    OJ 21651::Cow Hurdles(佛罗一德的变式)
    Oj 24260: Lilypad Pond (神奇广搜题,状态搜索)
    MongoDB常用语句
    MongoDB可视化工具之mongoBooster
    MongoDB的安装,mongod和mongo的区别
    数组遍历及其他方法
    MongoDB的安装与卸载与再安装
    MongoDB下载安装步骤+文件解析
  • 原文地址:https://www.cnblogs.com/jsgnadsj/p/3458050.html
Copyright © 2011-2022 走看看