这里以最大堆为例,最大堆满足父节点的值既大于左子节点,又大于右子节点。所对于一个最大堆而言,根节点保存着堆中最大的值。实际应用中,堆数据保存在一个数组中,且由于堆可以看成是一棵满二叉树,数组中的元素从上到下,从左到右一次填充二叉树(如下图),因此树中的父子节点在数组中的索引拥有固定的关系:
image.png

堆排序的基本思路是首先将待排序数组构建成一个最大堆,将堆顶的元素与堆的最后一个元素交换位置,同时将堆的尺寸减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);

image.png


helloweilei
130 声望2 粉丝

菜鸟一枚,各位道友多多指教!