美文网首页
2020-11-24 排序算法二(堆排序)

2020-11-24 排序算法二(堆排序)

作者: 宇宙区长李小无 | 来源:发表于2020-11-25 14:04 被阅读0次

    在前面,我们讲了二叉堆的实现,堆顶元素始终是最大或最小的,那么堆排序是怎么一回事呢?

    1. 根据数列生成一个最大堆(需要从小到大排序的时候)或最小堆;
    2. 交换堆顶和最后一个元素(假删除,自我调节中不需要处理交换到最后的栈顶元素),进行自我调节(downAdjust / upAdjust);
    3. 循环数列每个元素执行第2步;
      最终形成了一个层序遍历为从小到大(从大到小)数列的二叉树。
    构建堆

    把一个无序数列构建成一个二叉堆,最大最小而异,本质都是将其所有非叶子节点依次下沉,为什么这样就可以构建一个二叉堆呢?

    二叉堆的定义,父元素的值始终大于等于或者小于等于子节点的值,那么我们只需要将所有父节点都移动至满足该条件即可,所以所有的父节点都是非叶子节点。

    我们在构建堆的时候,该如何找到所有非叶子节点呢?

    我们都知道查找一个右子节点的父节点的索引为parentIndex = (childIndex - 2)/ 2,那么索引值最大的非叶子节点是多少?很明显,最后一个节点的父节点就是,即maxNotLeafNodeIndex = Math.floor((list.length - 1) / 2)。此时我们只需要从这个节点往前,遍历剩余的非叶子节点,将其依次下沉调整,即可得到一个二叉堆。

    function buildHeap(list) {
      const maxNotLeafNodeIndex = Math.floor((list.length - 1) / 2);
      for (let i = maxNotLeafNodeIndex; i >= 0; i--) {
        downAdjust(list, i, list.length);
      }
      return list;
    }
    
    通过构建最大堆来排序

    构建完堆以后,执行上面排序步骤的第2步第3步:

    function downAdjust(list, targetIndex, realLength) {
      let parentIndex = targetIndex;
      const target = list[parentIndex];
      let childIndex = Math.floor(2 * parentIndex + 1);
      while (childIndex <= realLength - 1) {
        if (childIndex + 1 <= realLength - 1 && list[childIndex+1] > list[childIndex]) {
          childIndex++;
        }
        if (list[childIndex] <= target) {
          break;
        }
        list[parentIndex] = list[childIndex];
        parentIndex = childIndex;
        childIndex = Math.floor(2 * parentIndex + 1);
      }
      list[parentIndex] = target;
    }
    function headSort(list) {
      // 生成最大堆
      const maxNotLeafNodeIndex = Math.floor((list.length - 1) / 2);
      for (let i = maxNotLeafNodeIndex; i >= 0; i--) {
        downAdjust(list, i, list.length);
      }
      // 遍历
      for (let i = 0; i < list.length; i++) {
        const max = list[0];
        // 最大的元素到数列末尾,不再参与堆的自我调整过程
        const usefulLen = list.length - 1 - i;
        list[0] = list[usefulLen];
        list[usefulLen] = max;
        downAdjust(list, 0, usefulLen);
      }
      return list;
    }
    
    堆排序的总结
    • 找到所有非叶子节点,构建二叉堆;
    • 从堆顶开始遍历,放置到堆底,再次进行自我调整,形成有序数列;
    • 时间复杂度稳定为O(nlogn),空间复杂度为O(1),不需要任何多余的空间来存储额外值。

    相关文章

      网友评论

          本文标题:2020-11-24 排序算法二(堆排序)

          本文链接:https://www.haomeiwen.com/subject/wtoiiktx.html