这里以最大堆为例,最大堆满足父节点的值既大于左子节点,又大于右子节点。所对于一个最大堆而言,根节点保存着堆中最大的值。实际应用中,堆数据保存在一个数组中,且由于堆可以看成是一棵满二叉树,数组中的元素从上到下,从左到右一次填充二叉树(如下图),因此树中的父子节点在数组中的索引拥有固定的关系:
堆排序的基本思路是首先将待排序数组构建成一个最大堆,将堆顶的元素与堆的最后一个元素交换位置,同时将堆的尺寸减1(即将最后一个位置的元素移除堆),调整剩余元素,使之保持堆的结构,然后重复上面的过程,直到堆中只有一个元素。
具体实现如下:
/**
* 以最大堆为例
*/
// 获取子节点的索引
const left = index => 2 * index + 1;
const right = index => 2 * (index + 1);
// 交换数组中的两个元素
const swap = (arr, i, j) => {
if (i === j) return;
const temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
};
// 对数组中的某个元素进行下沉操作
// 即如果当前元素小于某个子元素,则与该子元素交换位置,然后对子元素执行递归操作
function heapify(i, array, heapSize) {
const l = left(i);
const r = right(i);
let largest = i;
// 如果左子节点大于当前节点
if (l < heapSize && array[l] > array[i]) {
largest = l;
}
// 如果右子节点大于较大的值
if (r < heapSize && array[r] > array[largest]) {
largest = r;
}
if (largest !== i) {
swap(array, i, largest);
heapify(largest, array, heapSize);
}
}
// 构建最大堆
function buildHeap(array) {
// 从最后一个有子节点的元素开始, 即最后一个元素的父节点的索引
const start = Math.floor((array.length - 2) / 2);
for (let i = start; i >= 0; i--) {
heapify(i, array, array.length);
}
}
// 堆排序
function heapSort(array) {
buildHeap(array);
for (let i = array.length - 1; i > 0; i--) {
swap(array, 0, i);
heapify(0, array, i);
}
}
下面是测试代码及最终的运行结果:
const count = 20;
const arr = [];
for (let i = 0; i < count; i++) {
arr.push(Math.floor(Math.random() * 100));
}
console.log('before sort:', arr);
heapSort(arr);
console.log('after sort', arr);
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。