左神直通BAT算法之堆排序

Posted 程序员弹药库

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了左神直通BAT算法之堆排序相关的知识,希望对你有一定的参考价值。

堆排序

什么是堆

堆结构就是将一颗完全二叉树映射到数组中的一种存储方式:

大根堆和小根堆

当堆的每一颗子树(包括树本身)的最大值就是其结点时称为大根堆;相反,当堆的每一颗子树的最小值就是其根结点时称为小根堆。其中大根堆的应用较为广泛,是一种很重要的数据结构。

左神直通BAT算法之堆排序

heapInsert和heapify

大根堆最重要的两个操作就是 heapInsertheapify,前者是当一个元素加入到大根堆时应该自底向上与其父结点比较,若大于父结点则交换;后者是当堆中某个结点的数值发生变化时,应不断向下与其孩子结点中的最大值比较,若小于则交换。下面是对应的代码:

 
   
   
 
  1. //index之前的序列符合大根堆排序,将index位置的元素加入堆结构,但不能破坏大根堆的特性

  2. void heapInsert(int arr[],int index){

  3.    while (arr[index] > arr[(index - 1) / 2]) { //当该结点大于父结点时

  4.        swap(arr[index], arr[(index - 1) / 2]);

  5.        index = (index - 1) / 2;    //继续向上比较

  6.    }

  7. }


  8. //数组中下标从0到heapSize符合大根堆排序

  9. //index位置的值发生了变化,重新调整堆结构为大根堆

  10. //heapSize指的是数组中符合大根堆排序的范围而不是数组长度,最大为数组长度,最小为0

  11. void heapify(int arr[], int heapSize, int index){

  12.    int leftChild = index * 2 + 1;

  13.    while (leftChild < heapSize) {  //当该结点有左孩子时

  14.        int greatOne = leftChild + 1 < heapSize && arr[leftChild + 1] > arr[leftChild] ?

  15.                leftChild + 1 : leftChild;  //只有当右孩子存在且大于左孩子时,最大值是右孩子,否则是左孩子

  16.        greatOne = arr[greatOne] > arr[index] ? greatOne : index;//将父结点与最大孩子结点比较,确定最大值

  17.        if (greatOne == index) {

  18.            //如果最大值是本身,则不用继续向下比较

  19.            break;

  20.        }

  21.        swap(arr[index], arr[greatOne]);


  22.        //next turn下一轮

  23.        index = greatOne;

  24.        leftChild = index * 2 + 1;

  25.    }

  26. }

建立大根堆

 
   
   
 
  1. void buildBigRootHeap(int arr[],int length){

  2.    if (arr == NULL || length <= 1) {

  3.        return;

  4.    }

  5.    for (int i = 0; i < length; ++i) {

  6.        heapInsert(arr, i);

  7.    }

  8. }

利用heapify排序

前面做了那么多铺垫都是为了建立大根堆,那么如何利用它来排序呢?

对应代码实现如下:

 
   
   
 
  1. void heapSort(int arr[],int length){

  2.    if (arr == NULL || length <= 1) {

  3.        return;

  4.    }

  5.      //先建立大根堆

  6.    for (int i = 0; i < length; ++i) {

  7.        heapInsert(arr, i);

  8.    }

  9.       //循环弹出堆顶元素并heapify

  10.    int heapSize = length;

  11.    swap(arr[0], arr[--heapSize]);//相当于弹出堆顶元素

  12.    while (heapSize > 0) {

  13.        heapify(arr, heapSize, 0);

  14.        swap(arr[0], arr[--heapSize]);

  15.    }

  16. }


  17. int main(){

  18.    int arr[] = {9,7,1,3,6,8,4,2,5};

  19.    heapSort(arr, 9);

  20.    travles(arr, 9);

  21.    return 0;

  22. }

堆排序的优势在于无论是入堆一个元素 heapInsert还是出堆一个元素之后的 heapify都不是将整个样本遍历一遍( O(n)级别的操作),而是树层次上的遍历( O(logn)级别的操作)。

这样的话堆排序过程中,建立堆的时间复杂度为 O(nlogn),循环弹出堆顶元素并 heapify的时间复杂度为 O(nlogn),整个堆排序的时间复杂度为 O(nlogn),额外空间复杂度为 O(1)

出自:http://www.zhenganwen.top

已获授权


作者是前腾讯员工/现创业公司员工,致力于分享leetcode/剑指offer/算法题解/互联网时事/编程资源,觉得不错关注转发一下。



以上是关于左神直通BAT算法之堆排序的主要内容,如果未能解决你的问题,请参考以下文章

算法排序之堆排序

直通BAT算法精讲附程序源码

直通BAT面试算法精讲课2

数据结构十大排序算法+二分查找(左程云 左神版 全文2W字+ word很大,你忍一下~~)

重温基础算法内部排序之堆排序法

重温基础算法内部排序之堆排序法