一. 堆的概念
堆是一棵顺序存储的二叉树
大根堆(大顶堆):其中每个节点的值都不小于其子节点
小根堆(小顶堆):其中每个节点的值都不大于其子节点
二. 堆排序的过程
- 创建堆
- 调整堆
- 选取最值
- 再次调整堆
三.代码实现
let arr = [9, 1, 2, 8, 7, 3, 4, 6, 5]
function swap(arr, i, j){
let temp = arr[i]
arr[i] = arr[j]
arr[j] = temp
}
function BuildHeap(arr){
let len = arr.length
// 因为二叉树的特点, 父节点 i 左子节点 2i+1 右子节点 2i+2
let start = Math.floor(len/2) - 1
for(let i = start; i >= 0; i--){
heapify(arr, i, len)
}
}
function HeapSort(arr){
let len = arr.length - 1
// 1.创建堆
BuildHeap(arr)
for(let i = len; i > 0; i--){
swap(arr, 0, i)
heapify(arr, 0, i)
}
return arr
}
function heapify(arr, i, end){
let current = i
// 父节点 i 左子节点 2i+1 右子节点 2i+2
// 0
// 1 2
// 3 4 5 6
let left = 2*i + 1
let right = 2*i + 2
if(left < end && arr[current] < arr[left]){
current = left
}
if(right < end && arr[current] < arr[right]){
current = right
}
if(current != i){
swap(arr, i, current)
heapify(arr, current, end)
}
}
四. 堆排序的时间复杂度计算
时间复杂度为:O(nlogn)
- 创建堆时间复杂度 O(n)
- 外层循环时间复杂度 O(n)
- 内层 heapify 方法采用递归,最差的情况是每一层都要交换,所以时间复杂度为层数
求层数与节点数之间的关系,假设深度为 m 的完全二叉树
总共就有 2^m - 1 = n 的节点
所以 m = log<2> n 所以时间复杂度为 O(logn)
五. 堆排序适用的场景,和快速排序区别
-
快速排序、堆排序时间复杂度都为O(nlgn),且都是不稳定的排序方式。
-
快速排序是目前基于比较的内部排序中被认为是最好的方法,当待排序的关键字是随机分布时,快速排序的平均时间最短。
-
堆排序适用于优先队列,优先选出一部分序列的情况,可以选堆排序。