美文网首页小撒学编程
[小撒学算法]堆排序

[小撒学算法]堆排序

作者: 笨笨小撒 | 来源:发表于2018-01-18 19:04 被阅读0次

    小撒是一只好学的小鸭子,这天,小撒在学习算法

    二叉堆与最大堆

    二叉堆可以被视为完全二叉树,数组和二叉堆的表现形式可以互相转换:

    数组与二叉堆的转换

    从图中我们可以观察到二叉堆和数组的转换关系;同时也观察到,如何求一个节点的父节点、左右子节点的索引。

    而在最大堆(大根堆)中,需要满足在堆中任何节点为根的子堆中,根节点存储着此堆的最大值。如上图所示的,便是一个最大堆。

    调整最大堆

    现在我们来考虑从两个子最大堆合并最大堆的情况。

    最大堆合并调整过程

    假设我们要合并根节点为4的两个最大堆[16,14,7,2,8,1][10,9,,3],首先我们先找出4,16,10中的最大者,并交换416的位置。因为位置的交换,使得新形成的4,14,7可能违反了最大堆,因此需要进行判断。如此在每次交换后持续判断,直到交换至叶节点,或是新形成的子堆已经符合最大堆为止。

    那怎么建立只有2或3个元素的最小堆呢?只要把其中最大的元素交换到根就行啦。

    建堆

    建堆的过程,便是从下至上的堆合并调整过程。这里我们需要注意到的是,非叶节点开始与Math.floor(arr.length / 2),我们只需让i由此开始不断递减i来调整堆。

    堆排序(heapsort)

    在建堆过程后,数组的第一个元素(堆顶)就是最大的元素,我们在这里将其与数组的最后一个元素对换后将其取出数组,然后重新建立size1的最大堆,不断重复这个过程,就能一次取出数组从大至小的元素了。

    代码示例(js)

    // 调整堆
    const maxHeapify = (arr, root) => {
      let largest = root
      const left = 2 * root + 1
      const right = 2 * root + 2
    
      if (left < arr.length) {
        if (arr[largest] < arr[left]){
          largest=left
        }
        if (arr[largest] < arr[right]){
          largest=right
        }
        if (largest != root){
          swap(arr, root, largest)
          maxHeapify(arr, largest)
        }
      }
    }
    
    // 建堆
    const buildMaxHeap = function (arr) {
      for(let i = Math.floor(arr.length / 2) - 1; i >= 0; i--){
        maxHeapify(arr, i)
      }
    }
    
    // 堆排序
    const heapSort = function(arr){
      const rtn = []
      buildMaxHeap(arr)
      for (let i = arr.length - 1; i > 0; i--) {
        swap(arr, 0, i)
        rtn.unshift(arr.pop())
        maxHeapify(arr, 0)
      }
      rtn.unshift(arr[0])
      return rtn
    }
    

    小结

    堆排序的运行时间是O(n * log(n)),同时它是一种原地(in place)排序,只需额外开辟常数个存储空间。

    相关文章

      网友评论

        本文标题:[小撒学算法]堆排序

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