堆排序
首先!堆排序非常非常非常重要!一定要学会手写堆排序!!!
学习堆排序之前,我们要先学会两个前置知识点:形成大根堆(堆插入)、堆化
对应的英文是:heapInsert、heapify
大根堆介绍
通俗来说,大根堆就是根节点都大于子节点的完全二叉树
像下面这个图,每个根节点都大于子节点
补充一下:得搞懂 “二叉树”、“完全二叉树”、“满二叉树” 之类的概念 的区别
heapInsert
此操作就是将一个个数插入到 堆中
第一步,将新增加的数加到堆的末尾
第二步,将新增加的节点与他的父节点做比较,若子节点大于父节点,父子节点交换。依次重复此操作,直到此数字到达堆顶或不再大于它的父节点
操作结果:最大的数字被扔到最顶部
图示环节
代码环节
/**
* 一个个元素插入,形成大根堆
* @param index 数字处在数组的位置
* 数组 index 可以转化为二叉树,2i + 1 是左孩子,(i-1) / 2 是父节点
*/
public static void heapInsert(int[] arr, int index) {
//当前节点的值大于父节点的值,交换两个节点的位置
while (arr[index] > arr[(index-1) / 2]) {
swap(arr,index,(index-1) / 2);
index = (index-1) / 2;
}
}
heapify
这个操作的思路是:
1、将堆顶的最大值,和堆的最后一位交换位置,然后堆容量减1
2、然后再重新将剩余的内容变成大根堆
3、重复此操作,以此达到排序目的。
图示环节
数组乱序时:
构建大根堆:
交换堆首尾元素,然后堆大小 -1
此时数组的最后一位已经是有序的了!!!
然后利用此数组进行 heapify(堆化) 操作,重新变成大根堆
然后重复第一个步骤,将堆顶的最大值(首位),和堆的最后一位交换位置
在这里是 4 和 1 交换位置,然后对交换后的堆,进行再一次 heapify 操作,形成新的大根堆
数组的情况:
如此循环,直至排序完毕
代码环节
温馨提示:
左孩子下标是: i * 2 +1
右孩子下标是:左孩子下标 + 1
父节点下标是:( i -1 ) / 2
/**
* 在任意位置进行堆化操作,
* @param index 数字处在数组的位置,往下做 heapify
* @param heapSize 堆的大小。堆减至 0 说明大根堆已经全部弹出到数组中,管着左右孩子是否越界(数组的大小并不是堆的大小)
*/
public static void heapify(int[] arr, int index, int heapSize) {
int left = index * 2 + 1; // 左孩子下标
while (left < heapSize) { // 有孩子,左孩子没越界
// 左右孩子比较大小,较大数的下标,赋值给 largest 下标
int largest = left + 1 < heapSize && arr[left + 1] > arr[left] ? left + 1 : left;
// 孩子和父节点比大小
largest = arr[largest] > arr[index] ? largest : index;
if (largest == index) {
// 若自身大于左右孩子,则中断循环,不用继续比较了
break;
}
swap(arr,largest,index);
index = largest; // 往下移动
left = index * 2 + 1; // 刷新左孩子的下标
}
}
堆排序代码
public static void heapSort(int[] arr) {
if (null == arr || 2 > arr.length) {
return;
}
// 将一个个元素插入到二叉树中,形成大根堆
for (int i = 0; i < arr.length; i++) {
heapInsert(arr, i); //O(logN)
}
int heapSize = arr.length;
// 将数组末尾的元素和头部元素调换位置
// 因为上面数组已经是大根堆了,所以头部的数字肯定是最大的,然后就往最后面交换!
swap(arr, 0, --heapSize);
// 一直重复这个步骤,直到 heapSize 为 1
while (heapSize > 0) {
heapify(arr, 0, heapSize);
swap(arr, 0, --heapSize);
}
}
public static void heapInsert(int[] arr, int index) {
//当前节点的值大于父节点的值,交换两个节点的位置
while (arr[index] > arr[(index-1) / 2]) {
swap(arr,index,(index-1) / 2);
index = (index-1) / 2;
}
}
public static void heapify(int[] arr, int index, int heapSize) {
int left = index * 2 + 1;
while (left < heapSize) {
int largest = left + 1 < heapSize && arr[left + 1] > arr[left] ? left + 1 : left;
largest = arr[largest] > arr[index] ? largest : index;
if (largest == index) {
break;
}
swap(arr,largest,index);
index = largest;
left = index * 2 + 1;
}
}
public static void swap(int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}