堆排序 就是利用大根堆或者小根堆保证堆顶处一定是最大值或者最小值
先验知识:
- 使用数组保存一颗完全二叉树, 完全二叉树的节点 N = heapSize
- 二叉树的高度为log(heapSize) = logN
- 一个节点i, 则i的左孩子是 (2 * i + 1), 右孩子是 (2 * i + 2), 父亲节点是((i - 1) / 2)
- heapInsert(): 向堆中插入新的数字a到heapSize处, 让a一直和其父亲比较大小, 超过就取代父亲节点, 直到找到合适位置
- heapify(index): 堆化, 从index向左右孩子中较大的一个比较, 如果小于则交换, 直到找到合适的位置
1)即先将数组的每个元素一次heapInsert()
2)然后将堆顶元素和最后一个元素交换位置, heapSize–
3)此时继续对堆顶元素做heapify()操作, 然后继续交换
4)重复上面步骤, 每次都会将一个元素放到最后, 完成排序
注意:
- 顺序排序需要维护大根堆, 逆序排序需要维护小根堆, 因为需要交换堆顶元素到最后, 所以其实是相反的
- 时间复杂度是NlogN, 每次heapInsert()或者heapify()都是logN的, 然后一共需要遍历两次数组即2N
Java大根堆实现顺序排序模版
import java.util.Arrays;
public class HeapSort {
public static void main(String[] args) {
int[] arr = {0, -3, 5, 3, 2, 565, 67, 4, 2, 3, 23, 423, 43, 53, 52, 6, 4, 2, 32, 452, 23, 23, 5, 54, 9, 6};
System.out.println(Arrays.toString(arr));
heapSort(arr);
System.out.println(Arrays.toString(arr));
}
// 默认使用大根堆 做顺序排序
private static void heapSort(int[] arr) {
if (arr.length < 2) {
return;
}
// 先做一次heapInsert操作
for (int i = 0; i < arr.length; i++) {
heapInsert(arr, i);
}
// 再交换堆顶元素到最后面 然后对交换上来的元素继续做heapify()
int heapSize = arr.length - 1;
while (heapSize > 0) {
// 交换堆顶元素
swap(arr, 0, heapSize);
heapSize--; // 最后一个已经排序好了 不用动它
// 对交换到堆顶的元素做heapify
heapify(arr, 0, heapSize);
}
}
// 对heapSize的元素做heapInsert()操作,向上比较 大根堆
private static void heapInsert(int[] arr, int heapSize) {
// 取代比它小的父亲节点
while (arr[heapSize] > arr[(heapSize - 1) / 2]) {
swap(arr, heapSize, (heapSize - 1) / 2);
heapSize = (heapSize - 1) / 2;
}
}
// 对小于heapSize的任意节点index做heapify,向下比较 大根堆
private static void heapify(int[] arr, int index, int heapSize) {
// 先判断是否有孩子
int left = 2 * index + 1;
while (left <= heapSize) {
// 此时左孩子left肯定存在, 看右孩子left + 1是否存在
int largest = left + 1 <= heapSize && arr[left + 1] > arr[left]
? left + 1 : left;
// 比较父亲和较大的孩子之间最大的一个
largest = arr[index] > arr[largest] ? index : largest;
// 如果 父亲更大 表示这个位置合适 不用交换
if (largest == index) {
break;
}
// 孩子更大 交换
swap(arr, index, largest);
// 将当前的index变成较大的孩子节点的索引
index = largest;
// 继续判断左孩子
left = 2 * index + 1;
}
}
private static void swap(int[] arr, int i, int j) {
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
}