• 堆排序


      堆排序是我们熟知的常用的排序算法。

      首先先介绍一下什么是堆排序。堆指的并不是我们数据结构上分配内存的堆栈,在这里指的是大顶堆和小顶堆。大顶堆是指根节点数值大于左右孩子节点的完全二叉树,也有可能是满二叉树。总之,就是一颗存贮数值的树。我们可以利用根节点大于孩子节点这一特性对一组数组建立堆,不断取出根节点再建立大顶堆的过程来完成对数组排序。

      对于大顶堆,可以用这样的数学角度考虑,即a[i]>a[2i],且a[i]>a[2i+1],也就是上面所说的根节点的数值大于左右孩子节点的数值。对于小顶堆则反之。

      知道了大顶堆的概念,如何建立大顶堆呢?可以这样考虑。大顶堆父亲节点肯定大于孩子节点,所以首先对一个节点与其左右孩子节点比较,选取数值最大的那个,交换数值变成大顶堆。如果过程中交换了数值,说明被交换的那个孩子还需要再对其考虑一次是否为大顶堆,再对它进行一次比较。如此一来,我们完全可以利用递归调用自身的方式进行建堆。

      上面的第一步只是保证那一部分是大顶堆,如何保证整棵树都是大顶堆的形式?这就需要我们把每一个非叶子节点比较一次。但是按照什么样的顺序呢?为了保证整个树为大顶堆,这就需要我们从下而上建立大顶堆。请你自己思考一下这是为什么。也就是从n/2(n指的是总长度)到1逐个比较排序建堆。当然,对于叶子节点根本无需考虑,因为总之它的父母会主动和他们比较。

      以上两步工作是完成建立大顶堆,可是我们的堆排序呢?接下来,我们就是巧妙的利用大顶堆的性质,对数组进行交换排序。具体操作为:首先交换a[0]和a[n],即交换最大的到最后。然后再把剩余的1到n-1的堆重新进行堆排序。反复循环。这样就可以保证堆的第一个元素永远是最大的。不断交换第一个元素与最后一个,完成从小到大排序。

      十分简单而又十分优雅的排序,但是我习惯上用0作为数组的第一个下标,所以各种下标处理就需要格外谨慎。左右孩子节点处理也要十分小心。已下为具体的C++程序代码,CB已经完美运行。

     1 #include<iostream>
     2 using namespace std;
     3 
     4 int a[10] = {4,1,3,2,16,9,10,14,8,7};
     5 int len = sizeof(a)/sizeof(int);
     6 
     7 inline int PARENT(int i){
     8     return i/2;
     9 }
    10 
    11 inline int LEFT(int i){
    12     return 2*i+1;
    13 }
    14 
    15 inline int RIGHT(int i){
    16     return 2*i+2;
    17 }
    18 
    19 void exchange(int &a, int &b){
    20     int temp = a;
    21     a = b;
    22     b = temp;
    23 }
    24 
    25 void maxHeapify(int a[], int i){    //put the index i into the right place
    26     int maxIndex;
    27     if(LEFT(i) < len && a[LEFT(i)] > a[i]){
    28         maxIndex = LEFT(i);
    29     }else{
    30         maxIndex = i;
    31     }
    32     if(RIGHT(i) < len && a[RIGHT(i)] > a[maxIndex]){
    33         maxIndex = RIGHT(i);
    34     }
    35 
    36     if(maxIndex != i){
    37         exchange(a[i], a[maxIndex]);
    38         maxHeapify(a, maxIndex);
    39     }
    40 }
    41 
    42 void buildMaxHeap(int a[]){
    43     int l;
    44     if(len%2 == 0)
    45         l = len/2 - 1;
    46     else
    47         l = len/2;
    48     for(int i = l; i >= 0; --i)
    49         maxHeapify(a, i);
    50 }
    51 
    52 void heapSort(int a[]) {
    53     buildMaxHeap(a);
    54     for(int i = len - 1; i != 0; --i){
    55         exchange(a[0], a[i]);
    56         --len;
    57         buildMaxHeap(a);
    58     }
    59 }
    60 int main() {
    61     buildMaxHeap(a);
    62     for(int i = 0; i < 10; ++i)
    63         cout<<a[i]<<" ";
    64     cout<<endl;
    65     heapSort(a);
    66     for(int i = 0; i < 10; ++i)
    67         cout<<a[i]<<" ";
    68 
    69     return 0;
    70 }
  • 相关阅读:
    Java自学-多线程 常见线程方法
    Java自学-多线程 启动一个线程
    Java自学-Lambda 聚合操作
    Java自学-Lambda 方法引用
    Java自学-Lambda 概念
    Java自学-泛型 泛型转型
    Java自学-泛型 通配符
    Java自学-泛型 支持泛型的类
    <VCC笔记> 关于Assertion
    <VCC笔记>VCC简介与安装
  • 原文地址:https://www.cnblogs.com/zhaoyansheng/p/5164293.html
Copyright © 2020-2023  润新知