HeapSort

254 阅读4分钟

堆排序

堆数据结构是一种数组对象,可以视为一个完全二叉树,每一层都是填满的。

下图展示了一个堆以及它的一些特性:

特性:当我们知道数组对象i的时候我们就可以确定它的父元素索引在i/2,左子树在2i,右子树在2i + 1。

length[A]:是数组中的元素个数

HeapSize[A]:存放在A中的堆的元素个数

二叉堆有两种,最大堆和最小堆。

最大堆,在最大堆中,最大堆特性是指除了根以外的每个结点i,最大元素在根节点,有:

A[PARENT(i)] >= Ai

最小堆,在最小堆中,最小堆特性是指除了根以外的每个结点i,最小元素在跟几点,有:
A[PARENT(i)] <= Ai

堆的基本操作:

  • MAX-HEAPIFY过程,运行时间为O(lgn),含义为i作为根节点的最大堆,是保持最大堆的关键性质。

  • BUILD-MAX-HEAP过程,数组线性输入,构造出最大堆。

  • HEAPSPORT过程,运行时间为O(nlgn),对一个数组原地进行排序

  • MAX-HEAP-INSERT

堆基本操作的实现:

  1. MAX-HEAPIFY算法导论中描述:

递归可以理解为A[i],LEFT[i],RIGHT[i],思路通过largest记录一个最大值,如果largest=说明没有变化,如果largest != i 说明改变交互值,如果i和largest进行了值交换说明largets下面的叶子节点可能也要变动,所以进行递归。

js实现:

function MaxHeapify(arr, i) {
    const left = 2 * i + 1;
    const right = 2 * i + 2;
    let largest = i;
    if (left < HeapSize && arr[left] > arr[largest]) {
        largest = left;
    }
    if (right < HeapSize && arr[right] > arr[largest]) {
        largest = right;
    }
    if (largest !== i) {
        swap(arr, i, largest);
        MaxHeapify(arr, largest);
    }
}
function swap(arr, i, largest) {
    const temp = arr[i];
    arr[i] = arr[largest];
    arr[largest] = temp;
}
  1. BUILD-MAX-HEAP算法导论中描述:

建堆的过程是从底层一直到根这样建立的过程,依赖于上面的MaxHeapify这个函数。首先我们算出最底层的i的范围[m-n],排除,因为它没有叶子节点了。然后从m-1开始依次递归调用MaxHeapify构建。

计算最后一层的[m-n]范围,可以理解为:

完全二叉树可以理解为一个等比数列,S = 2^(h + 1) - 1是等比数列求和公式。

通过证明我们知道[m,n]范围是[(n + 1) / 2, n],所以我们需要递归小于(n + 1) / 2部分的i值。

js实现:

function BuildMaxHeap(arr) {
    HeapSize = arr.length;
    for (let i = Math.floor((HeapSize + 1) / 2); i >= 0; i --) {
       MaxHeapify(arr, i); 
    }
}
  1. HEAPSORT算法中定义描述(堆排序):

思想:在最大堆中根是最大的,可以通过每次取出根结点放在数组最后,然后将根节点数据换成最底层的结点,重新生成最大堆,没递归一次就是根结点都是最大值,一直递归到最后,即可完成排序。

js实现:

function HeapSort(arr) {
    BuildMaxHeap(arr);
    for (let i = arr.length - 1; i >= 0; i --) {
        swap(arr, 0, i);
        HeapSize --;
        MaxHeapify(arr, 0);
    } 
}

综上:

/*
 * @Author: xiuquanxu
 * @Company: kaochong
 * @Date: 2020-03-27 13:17:59
 * @LastEditors: xiuquanxu
 * @LastEditTime: 2020-03-27 13:26:52
 */
let HeapSize = 0;
function MaxHeapify(arr, i) {
    const left = 2 * i + 1;
    const right = 2 * i + 2;
    let largest = i;
    if (left < HeapSize && arr[left] > arr[largest]) {
        largest = left;
    }
    if (right < HeapSize && arr[right] > arr[largest]) {
        largest = right;
    }
    if (largest !== i) {
        swap(arr, i, largest);
        MaxHeapify(arr, largest);
    }
}

function swap(arr, i, largest) {
    const temp = arr[i];
    arr[i] = arr[largest];
    arr[largest] = temp;
}

function BuildMaxHeap(arr) {
    HeapSize = arr.length;
    for (let i = Math.floor((HeapSize + 1) / 2); i >= 0; i --) {
       MaxHeapify(arr, i); 
    }
}

function HeapSort(arr) {
    BuildMaxHeap(arr);
    for (let i = arr.length - 1; i >= 0; i --) {
        swap(arr, 0, i);
        HeapSize --;
        MaxHeapify(arr, 0);
    } 
}

const test = [3,5,1,2,4,7,8,3,0];
HeapSort(test);
console.log(test);

eg1:在高度为h的堆中,最多和最少的元素个数是多少?

我们可以发现堆实际上是一种等比数列,1,2,4,8。。。。2^n   

当h为1的时候,2^0   
当h为2的时候,2^1
......
当为h的时候,2^(h-1)  
公比为2
所以总共元素个数为:  
公式一:s = 2^0 + 2^1 + ..... + 2^(h - 1) 
公式二:s * 2 = 2^1 + ..... + 2^(h - 1) + 2^h
公式二 - 公式一 = s*2 - s = 2^h - 2^0
s = 2^h - 1