作者: 韩绝交 | 来源:发表于2018-01-18 19:39 被阅读0次

http://bubkoo.com/2014/01/14/sort-algorithm/heap-sort/

1 什么是堆?

堆(二叉堆)可以视为一棵完全的二叉树,完全二叉树的一个“优秀”的性质是,除了最底层之外,每一层都是满的,这使得堆可以利用数组来表示(普通的一般的二叉树通常用链表作为基本容器表示),每一个结点对应数组中的一个元素。

堆与数组关系,当下标从1开始

对于给定的某个结点的下标 i,可以很容易的计算出这个结点的父结点、孩子结点的下标:

  • Parent(i) = i/2,i 的父节点下标
  • Left(i) = 2i,i 的左子节点下标
  • Right(i) = 2i + 1,i 的右子节点下标
当下标从0开始

当下标从0开始,几个计算公式也要作出相应调整:

  • Parent(i) = (i-1)/2,i 的父节点下标
  • Left(i) = 2i + 1,i 的左子节点下标
  • Right(i) = 2(i + 1),i 的右子节点下标
2 最大堆与最小堆
  • 最大堆中的最大元素值出现在根结点(堆顶)
  • 堆中每个父节点的元素值都大于等于其孩子结点(如果存在)
最大堆
  • 最小堆中的最小元素值出现在根结点(堆顶)
  • 堆中每个父节点的元素值都小于等于其孩子结点(如果存在)
最小堆
3 堆排序原理

1 插入
每次插入都是将新数据放在数组最后,接着从下至上进行交换。
i为插入的结点索引,也是堆数组插入后的有效长度-1

void MinHeapFixup(int a[], int i)  
{  
    for (int j = (i - 1) / 2; (j >= 0 && i != 0)&& a[i] > a[j]; i = j, j = (i - 1) / 2)  
        int tmp = a[i];
        a[i] = a[j];
        a[j] = tmp;
}  

2 删除
或称重排。按定义,堆中每次都只能删除第0个数据。
为了便于重建堆,实际的操作是将最后一个数据的值赋给根结点,
然后再从根结点开始进行一次从上向下的调整。
i为需要调整的结点,n为结点总数
正常删除时,首先把最后的元素覆盖掉第0个元素,数组长度n-1,i=0

 void HeapDelete(int a[], int i, int n){
        while(i < n){
            int max = i;
            int left = 2 * max + 1;
            int right = 2 * (max + 1);
            if(a[max] < a[left]){
                max = left;
            }
            if(a[max] < a[right]){
                max = right;
            }
            if(max != i){
                int tmp = a[i];
                a[i] = a[max];
                a[max] = tmp;
                i = max;
            }else {
                break;
            }
        }
    }

3 创建堆
创建堆是将一个数组想象成完全二叉树,从倒数第一个父节点开始做堆重排(即堆删除后的重排操作),然后当前结点不断往上移动直到根节点

void HeapBuild(int[] a, int n) {
        int i;
        int parent = ((n - 1) / 2);
        for (i = parent; i >= 0; i--) {
            HeapDelete(a, i, n);
        }
 }

4 堆排序
使用最小堆排序后是递减数组,要得到递增数组,可以使用最大堆。
第一次将A[0]与A[n - 1]交换,再对A[0…n-2]重新恢复堆。第二次将A[0]与A[n – 2]交换,再对A[0…n - 3]重新恢复堆,重复这样的操作直到A[0]与A[1]交换。由于每次都是将最小的数据并入到后面的有序区间,故操作完成后整个数组就有序了。有点类似于直接选择排序。

void HeapSort(int[] a, int n) {
        for (int i = n - 1; i >= -1; i++){
            int tmp = a[i];
            a[i] = a[0];
            a[0] = tmp;
            HeapDelete(a,0,i);
        }
 }

由于每次重新恢复堆的时间复杂度为O(logN),共N - 1次重新恢复堆操作,再加上前面建立堆时N / 2次向下调整,每次调整时间复杂度也为O(logN)。二次操作时间相加还是O(N * logN)。故堆排序的时间复杂度为O(N * logN)。

相关文章

  • 堆 - 堆的应用

    堆有三个典型的应用场景:实现优先队列、求 Top K 、求中位数 实现优先队列 优先队列:队列的性质是先进先出,但...

  • 二叉堆是一棵满二叉树,父节点的值大于子节点。 用数组存储二叉堆,假如一个节点的下标为k,那么这个节点的左孩子就是2...

  • 应用: 排序,从小到大用最大堆,从大到小用最小堆 选出元素中的 top k 个top k 个最小数:数组前k个元素...

  • 完全二叉树 二叉堆 二叉堆有最大堆和最小堆的区别,最大堆只保证每个节点的父节点大于当前节点,但不保证上一层节点的值...

  • 堆的定义: n个元素序列{k1,k2,...,ki,...,kn},当且仅当满足下列关系时称之为堆: (ki...

  • http://bubkoo.com/2014/01/14/sort-algorithm/heap-sort/ 1 ...

  • 堆 …

    南山南,北山北,南山有谷堆,北山有花蕾,山坡下,大道中,野树停在石堆,秋风送,冷雪飘,旅途空旷叶儿飞,时间漫,皱纹...

  • 题目:100w个数中找出最大的100个。 维护一个100个元素的小根堆即可。 或者直接维护一个用来存储当前最大的1...

网友评论

      本文标题:

      本文链接:https://www.haomeiwen.com/subject/lplwoxtx.html