堆排序
堆排序在 top K 问题中使用比较频繁,并且经常被用来实现优先级队列。堆排序是采用二叉堆的数据结构来实现的,虽然实质上还是一维数组。二叉堆是一个近似完全二叉树 。
堆的定义如下:
- $L(i) \leq L(2i)$ 且 $L(i) \leq L(2i+1)$ (小根堆)
- $L(i) \geq L(2i)$且 $L(i) \geq L(2i+1)$ (大根堆)
堆排序的关键是构造初始堆,对初始序列建堆,就是一个反复筛选的过程,因为是近似于完全二叉树,所以从第$\lfloor n/2 \rfloor$为根的子树开始调整(对于大根堆,根节点的关键字和其左右子女中关键字相比较大的进行交换)之后依次对各个节点$\lfloor n/2 \rfloor -1 ~ 1$为根的子树进行筛选。
实例
对关键字序列:53、17、 78、 9、 45、 65、 87 、32 构建大根堆
插入
插入是从尾部插入(当前堆有n个元素,在第n+1位置中插入),然后调整
删除
删除是从根节点开始删除,删除之后再进行调整
Java代码实现
public void buildMaxHeap(int[] nums) {
int length = (nums.length - 1) / 2;
// 从n/2开始构建初始堆
for (int i = length; i >= 0; i--) {
adjustDown(nums, i, nums.length-1);
}
}
private void adjustDown(int[] nums, int k, int len) {
nums[0] = nums[k];
// 以第k个节点为根的子树进行调整
for (int i = 2 * k; i <= len; i *= 2) {
// 寻找子树中的最值
if(i < len && nums[i] < nums[i+1]){
i ++;
}
if(nums[0] >= nums[i]){
break;
}
else {
nums[k] = nums[i];
k = i;
}
}
nums[k] = nums[0];
}