算法描述
1 每次都将数组建立为二叉堆,构建成 [大或小] 顶堆,堆顶 (索引值为 0) 的值一定是整个数组里最[大或小] 的。
2 弹出堆顶的值到新数组, 旧数组长度减少 1。
3 重复 1 ,2完成排序, 重复新建堆的次数等于旧数组长度递减 1。
方便理解的写法,后续出优化性能版。
'use strict'
var arr = [1010, 1000, 999, 666, 500, 444, 300, 123, 91, 50, 22, 12, 10, 5, 2, 1, 0, 0];
var arr2 = [0, 0, 1, 2, 5, 10, 12, 22, 50, 91, 123, 300, 444, 500, 666, 999, 1000, 1010];
function heapSort(arr) {
//根据条件交换元素位置
function swap(arr, n1, n2) {
if (arr[n1] < arr[n2]) {
[arr[n1], arr[n2]] = [arr[n2], arr[n1]];
exchange++;
}
}
// 把二叉树建立为二叉堆,
// idx = Math.floor(arrLength / 2) - 1, 数组索引从0起要减1。
// 否则 左右子节点索引值都会超出范围,多一次无用的循环。
function heap(arr, idx) {
var left = (idx * 2) + 1,
right = (idx * 2) + 2;
// 左右两个子节点比较大小,小的放左边。
swap(arr, left, right);
// 左子节点与父节点比较大小,小的成为新父节点。
swap(arr, idx, left);
}
var arrLength = arr.length, count = 0, exchange = 0, newArr = [];
// 建立二叉堆的次数等于数组长度
while (arrLength) {
let idx = Math.floor(arrLength / 2) - 1;
// 把最小值放到跟节点,也就是索引值为 0 的位置。
for (let i = idx; i >= 0; i--) {
heap(arr, i)
count++;
}
//把 0 索引值弹出, 放到新数组中。
newArr.push(arr.shift());
arrLength--;
}
// 输出辅助计数
console.log(
'forLoop: ' + count,
'swap: ' + exchange
);
return newArr;
}
console.log(heapSort(arr2));
forLoop: 81 swap: 70
[ 1010, 1000, 999, 666, 500, 444, 300, 123, 91, 50, 22, 12, 10, 5, 2, 1, 0, 0 ]