最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 堆排序 (一,shift up、shift down、heapSort)

    正文概述 掘金(? 蘑菇放辣椒)   2021-03-10   585

    先来介绍一下普通队列和优先队列:

    普通队列:先进先出,后进后出。

    优先队列:出队顺序和入队顺序无关;和优先级有关。

    优先队列

    为什么要使用优先队列?举个例子:

    在N个元素中选出前M个元素。

    • 排序的时间复杂度:NlogN
    • 优先队列的时间复杂度:NlogM

    可见优先队列的速度是比普通队列快的。

    堆的数据结构是二叉树。就像下面这两张图: 堆排序 (一,shift up、shift down、heapSort) 堆排序 (一,shift up、shift down、heapSort) 堆中某个节点的值,总是不大于父节点的值,这样的二叉树结构,叫做完全二叉树。

    所以,堆总是一棵完全二叉树。

    最大值在顶的,叫做最大堆。最小值在顶的叫做最小堆。

    特点:

    • 某个节点的值,总是不大于父节点的值。
    • 最后一行可能不会是满的,但是一定会在左侧。

    堆这种数据结构,依然是用数组来存储的,节点是有规律的,看下面这张图: 堆排序 (一,shift up、shift down、heapSort)

    • 左面的节点从顶开始,往下,都是乘以二。
    • 右边的节点从顶开始,往下,都是乘2加1。

    这是将顶点62的位置规定为1了,如果是完全符合数组的结构,62的位置应该是0。

    这样规律就会发生改变。一定要注意。

    最大堆添加元素

    假设我们现在有一个最大堆,就是上图那个数组["",62,41,30,28,16,22,13,19,17,15]

    我们现在要往这个最大堆中添加一个元素52,插入到正确的位置。 堆排序 (一,shift up、shift down、heapSort)

    思考:
    • 既然是数组,肯定是先将52放入到数组的末尾了。
    • 那52的下标就是11。
    • 放入之后这个堆就不符合最大堆的定义,因为52比16大。
    • 既然比16大就要和16交换位置。所以要找到16的索引。
    • 找索引的办法就是通过我们之前说的,52索引是奇数11,(11-1)/2就是16的索引。
    • 找到之后交换位置就可以了。

    看下面的代码:

    let arr = ["", 62, 41, 30, 28, 16, 22, 13, 19, 17, 15]
    function main(arr, num) {
        arr.push(num)            
        let maxIndex = arr.length - 1            
        let k = Math.floor(maxIndex / 2)                
        // 考虑边界值                
        while (k > 1 && arr[k] < arr[maxIndex]) {                        
            // 交换位置                                
            [arr[k], arr[maxIndex]] = [arr[maxIndex], arr[k]]                     
            maxIndex = k                        
            k = Math.floor(maxIndex / 2)            
        }            
        return arr
    }
    console.log(main(arr, 52))
    

    最大堆取出一个元素(Shift Down)

    现在我们的数组是:[ '', 62, 52, 30, 28, 41, 22, 13, 19, 17, 15, 16 ]

    依然满足最大堆条件,从堆中取出一个元素,让它依然满足最大堆。 堆排序 (一,shift up、shift down、heapSort)

    条件:
    • 从堆中取出一个元素,只能取根节点的元素,也就是62这个元素。
    思考:
    • 当我们从最大堆中出去根节点的元素。
    • 我们的最大堆中就相当于少了一个元素,如何填补这个元素?
    • 将最后一位的元素直接放在第一位,然后它下面的两个节点进行比较。
    • 谁大就与谁交换位置。这样就保证了,换上来的数比16和30都大。满足最大堆性质。
    • 然后继续不断比较。
    // shift down
    let arr = ['', 62, 52, 30, 28, 41, 22, 13, 19, 17, 15, 16];
    function shiftDown(arr, k) {
      	arr.splice(1, 1)
    	let array = arr.splice(arr.length - 1, 1)
    	arr.splice(1, 0, array[0])
    	let count = arr.length
    	// ['', 16, 52, 30, 28, 41, 22, 13, 19, 17, 15]
    	while (k * 2 <= count) {
    	  let j = k * 2
    	  // 可能和左孩子交换位置
    	  // 判断是否存在右孩子
    	  // 右孩子与左孩子比较如果右边大于左
    	  if (j + 1 <= count && arr[j + 1] > arr[j]) {
    		  j += 1
    	  }
    	  if (arr[k] >= arr[j]) {
    		  break
    	  }
    	  [arr[k], arr[j]] = [arr[j], arr[k]]
    	  k = j
    	}
    	return arr
    }
    console.log(shiftDown(arr, 1));
    

    堆排序

    我们知道了什么是最大堆,也知道如何添加一个元素后排好序,还知道取出一个元素后如何排好序。 那,如何让一个最大堆,完成从小到大的排序,或者从大到小的排序呢?

    思考:
    • Shift Down这步,如果将每次取出来的最大值,return 出去。
    • 剩下的排序,重复 return 出去这步操作。
    • 就解决了这样的问题。
    • 如果添加了一个新的值,在数组中,就shift up。
    • 再shift down。
    // heapSort1 堆排序
    let arr = ['', 62, 52, 30, 28, 41, 22, 13, 19, 17, 15, 16];
    // 交换位置函数
    function swap(arr, leftIndex, rightIndex) {
      [arr[leftIndex], arr[rightIndex]] = [arr[rightIndex], arr[leftIndex]]
    }
    // 将最大堆 shiftDown
    // return 每次取出的最大值 spliceArray
    // 剩下的排序,之后继续取出最大值
    function shiftDown(arr, k) {
      let array = arr.splice(arr.length - 1, 1);
      let spliceArray = arr.splice(1, 1)
      arr.splice(1, 0, array[0]);
      while (k * 2 <= arr.length) {
        let j = k * 2;
        if (j + 1 <= arr.length && arr[j + 1] > arr[j]) {
          j += 1;
        };
        // 小心这里有 undefined, 所以加入了!arr[j]判断
        if (!arr[j] || arr[k] >= arr[j]) {
          break;
        }
        swap(arr, k, j)
        k = j;
      };
      return spliceArray;
    }
    function heapSort1(arr) {
      let array = []
      for (let i = 1; i < arr.length; i++) {
        // 遍历的方式,取出最大值,将剩下的排序,直到结束
        array.push(...shiftDown(arr, 1))
      }
      arr.splice(0, 1)
      array = [...array, ...arr]
      return array;
    }
    console.log(heapSort1(arr)) // [62, 52, 41, 30, 28, 22, 19, 16, 17, 15, 13]
    

    总结:

    这只是一个最大堆的排序方式,如果不是最大堆,你可能会选择快速排序或者插入排序等。

    但是接下来要思考,任意数组,如何生成一个最大堆?

    要思考这个的原因是:数据最大表示这个数据优先级最高,取出这个数据之后,接下来的优先级如何确定?是不是还是需要用到shift down?

    是不是觉得,算法原来不是没有用处的,而是你没用到,或者不知道怎么用?

    让我们一起继续学习吧。。。。


    起源地下载网 » 堆排序 (一,shift up、shift down、heapSort)

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元