JavaScript数据结构与算法学习之堆排序算法

94 阅读2分钟

堆排序(Heapsort)是指利用堆这种数据结构所设计的一种排序算法。堆排序的平均时间复杂度为 Ο(nlogn)。堆是一种特殊的二叉树,近似完全二叉树,同时满足所有的节点都大于等于(最大堆)或小于等于(最小堆)每个它的子节点。
堆结构实现参考

1. 算法步骤

  1. 使用数组结构构建最大堆或最小堆;
  2. 将堆的根节点(最大/小值)和堆的最后一层某个叶子节点交换位置,然后将堆的大小减一;
  3. 将新的堆结构堆化,即重新构建为最大/小堆;
  4. 重复步骤二和步骤三,直到堆的大小为一。

2. 图片演示

image.png

3. 代码实现

本文使用的typescript实现,有兴趣的同学可以自己转换为JavaScript试试。

/**
 * 堆排序,改变原数组
 * @param arr 待排序数组
 * @param compareFn 比较函数: a > b 升序(构建最大堆),b > a 降序(构建最小堆)
 * @returns 
 */
function heapSort<T = unknown>(arr: T[], compareFn: (a: T, b: T) => boolean) {
  /**
   * 通过下移操作堆化:找出自身节点和左右子节点中的最大/小节点,然后交换位置,保证自身大于等于或小于等于子节点
   * @param idx 需要堆化的节点位置
   * @param size 堆在数组中结束位置即堆的大小
   * @param simpleArr 完整数组
   */
  const siftDown = (idx: number, size: number, simpleArr: T[]) => {
    let tempIdx = idx;
    const leftIdx = tempIdx * 2 + 1;
    const rightIdx = tempIdx * 2 + 2;

    if (leftIdx < size && compareFn(simpleArr[leftIdx], simpleArr[tempIdx])) {
      tempIdx = leftIdx;
    }

    if (rightIdx < size && compareFn(simpleArr[rightIdx], simpleArr[tempIdx])) {
      tempIdx = rightIdx;
    }

    if (tempIdx === idx) {
      return;
    }

    [simpleArr[idx], simpleArr[tempIdx]] = [simpleArr[tempIdx], simpleArr[idx]];
  };

  const buildHeap = (len: number, arr: T[]) => {
    // 一个数组只需要通过下移 (arr.length-1) / 2 次就可以完全堆化
    const begin = Math.floor((len - 1) / 2);
    for(let i = begin; i >= 0; i--) {
      siftDown(i, len, arr);
    }
  };

  let size = arr.length;
  while(size > 1) {
    buildHeap(size, arr);

    // 将堆的最大/小值和堆的最后一个元素交换位置,然后将堆的大小减一
    [arr[0], arr[size - 1]] = [arr[size - 1], arr[0]];
    size--;
  }

  return arr;
}

至此我们就实现了一个堆排序算法,有兴趣的童鞋可以自己试试哦~