带你读《图解算法小抄》十四、排序(15)https://developer.aliyun.com/article/1348135?groupCode=tech_library
总结思路
- 将无序序列构建成一个堆,根据升序降序需求选择大顶堆
- 将堆顶元素与末尾元素交换,将最大元素「沉」到数组末端
- 重新调整结构,使其满足堆定义,然后继续交换堆顶与当前末尾元素,反复执行调整、交换步骤,直到整个序列有序。
步骤
这里想说的几点注意事项(代码实现的关键思路):
- 第一步构建初始堆:是自底向上构建,从最后一个非叶子节点开始。
- 第二步就是下沉操作让尾部元素与堆顶元素交换,最大值被放在数组末尾,并且缩小数组的length,不参与后面大顶堆的调整
- 第三步就是调整:是从上到下,从左到右,因为堆顶元素下沉到末尾了,要重新调整这颗大顶堆。
代码模板
官方的代码模板我参考了下,比一些书籍写的都好记,所以可以参考作为堆排序的模板。
/** * @param {number[]} nums * @param {number} k * @return {number} */ // 整个流程就是上浮下沉var findKthLargest = function(nums, k) { let heapSize=nums.length buildMaxHeap(nums,heapSize) // 构建好了一个大顶堆 // 进行下沉 大顶堆是最大元素下沉到末尾 for(let i=nums.length-1;i>=nums.length-k+1;i--){ swap(nums,0,i) --heapSize // 下沉后的元素不参与到大顶堆的调整 // 重新调整大顶堆 maxHeapify(nums, 0, heapSize); } return nums[0] // 自下而上构建一颗大顶堆 function buildMaxHeap(nums,heapSize){ for(let i=Math.floor(heapSize/2)-1;i>=0;i--){ maxHeapify(nums,i,heapSize) } } // 从左向右,自上而下的调整节点 function maxHeapify(nums,i,heapSize){ let l=i*2+1 let r=i*2+2 let largest=i if(l < heapSize && nums[l] > nums[largest]){ largest=l } if(r < heapSize && nums[r] > nums[largest]){ largest=r } if(largest!==i){ swap(nums,i,largest) // 进行节点调整 // 继续调整下面的非叶子节点 maxHeapify(nums,largest,heapSize) } } function swap(a, i, j){ let temp = a[i]; a[i] = a[j]; a[j] = temp; }};
进行堆排序
findKthLargest(nums,nums.length)// 或者调整一下 let i=nums.length-1;i>=nums.length-k+1;的条件就行
5)复杂度
名称 |
最佳情况 |
平均情况 |
最坏情况 |
内存 |
稳定性 |
备注 |
堆排序 |
n log(n) |
n log(n) |
n log(n) |
1 |
否 |
|
6)参考资料
维基百科
带你读《图解算法小抄》十四、排序(17)https://developer.aliyun.com/article/1348133?groupCode=tech_library