堆排序
堆数据结构是一种数组对象,可以视为一个完全二叉树,每一层都是填满的。
下图展示了一个堆以及它的一些特性:

特性:当我们知道数组对象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
堆基本操作的实现:
- 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;
}
- 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);
}
}
- 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