https://art-design-ui.github.io/data-structure-algorithm/guide/%E5%A0%86%E6%8E%92%E5%BA%8F
基本介绍
堆排序是利用 堆 这种 数据结构 而设计的一种排序算法,它是一种选择排序,最坏 、最好、平均时间复杂度均为 O(nlogn),它是不稳定排序。
注意因为完全二叉树的性质,可以用数组表示对应的树结构(所以,堆排序过程中,你是看不到树这数据结构的,用数组进行映射了),这叫顺序存储
顺序存储二叉树
特点
- 第 n 个元素的 左子节点 为 2*n+1
- 第 n 个元素的 右子节点 为 2*n+2
- 第 n 个元素的 父节点 为 (n-1)/2
- 最后一个非叶子节点为 Math.floor(arr.length/2)-1
堆是具有以下性质的完全二叉树:
- 将待排序序列构造成一个大顶堆注意:这里使用的是数组,而不是一颗二叉树
- 此时:整个序列的 最大值就是堆顶的根节点
- 将其 与末尾元素进行交换,此时末尾就是最大值
- 然后将剩余 n-1 个元素重新构造成一个堆,这样 就会得到 n 个元素的次小值。如此反复,便能的得到一个有序序列。
堆排序介绍
第一步:构造堆结构
第二步:堆化变成最小(大)堆
从最后一个非叶子(有孩子的节点)节点开始调整,从左到右,从上到下进行调整。
发现他(1)的孩子(0)比他小,就和孩子交换位置,[2,0,3,4,1]
在从这个非叶子节点(0)的位置向上查看,发现他的父节点(2)比他大就交换位置,[0,2,3,4,1]
交换后的堆结构中,依然看这个非叶子节点(2)的位置,发现他的孩子(1)比他小,再次交互位置,[0,1,3,4,2]
堆化的过程就是让非叶子节点的值比他的孩子节点的值小(大)。
第三步:将堆顶元素与末尾元素进行交换
将堆顶元素与末尾元素进行交换,使其末尾元素最大。然后继续调整,再将堆顶元素与末尾元素交换,得到第二大元素。如此反复进行交换、重建、交换。
获取到对顶元素,记录并删除;
将最后一个位置上的值替换到堆顶,此时堆中的结构就是 [ 2, 1, 3, 4,];
再将堆顶元素(2)和他的孩子节点(1,3)比较,与最小的孩子节点进行互换,此时[1, 2, 3, 4];
交换完以后的节点(2)再和他下面的孩子节点比较,发现 4比他大,正好符合最小堆,不用交互了。得到了新的堆 [1, 2, 3, 4];
重复上述步骤,直到堆中只剩最后一个元素就结束。
总结思路
- 将无序序列构建成一个堆,根据升序降序需求选择大顶堆
- 将堆顶元素与末尾元素交换,将最大元素「沉」到数组末端
- 重新调整结构,使其满足堆定义,然后继续交换堆顶与当前末尾元素,反复执行调整、交换步骤,直到整个序列有序。
代码模板
/**
* @param {number[]} nums
* @param {number} k
* @return {number}
*/
// 整个流程就是上浮下沉
var findKthLargest = function(nums, k) {
let heapSize=nums.length
buildMaxHeap(nums,heapSize) // 1.构建好了一个大顶堆
// 2.进行下沉 大顶堆是最大元素下沉到末尾
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)
}
}
// 第i个元素和他的左右子节点比较,互换位置,构建过程和下沉过程都需要比较
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;
}
};
复杂度分析
堆化复杂度是 O(N),
排序:N个堆顶元素,每一个删除操作的复杂是 logN,
所以总共复杂度是 O(logN)
空间复杂度:堆里面有N个元素,所以O(N)