升序:最大堆,降序:最小堆
构造最大堆后,我们就知道了在当前无序区间里,最大的值是哪个,即堆顶元素,取出来放至有序区间,就像选择排序。
那么如何构造最大堆or最小堆?
::不断调整,最终形成
堆排序流程:1、初始化堆 2、获取堆顶元素,并移除它,重新构造堆
图解
以下动图均取自网络
-
初始化最大堆:先从底层开始比较交换构建堆 自 底 向 上 自底向上 自底向上
-
调整最大堆:先从顶层开始比较交换构建堆 自 顶 向 下 自顶向下 自顶向下
代码模板(升序)
public void get() {
int[] arr = new int[]{3, 4, 5, 1, 2, -1, 0, 9, 4, 2};
heapSort(arr);
for (int i : arr) {
System.out.println(i);
}
}
public void heapSort(int[] arr) {
int num = arr.length;
//初始化最大堆
for (int i = (num - 2) / 2; i >= 0; i--) {
adjustTheHeap(arr, i, num);
}
//1、将堆顶元素与无序区间末尾元素交换,如此无序区间末尾元素成为有序区间的一份子,无序区间长度-1
//2、经过第一步,最大堆已经打乱(堆顶元素不符合要求),重新调整为最大堆,注意:无序区间长度
for (int i = num - 1; i > 0; i--) {
swap(arr, 0, i);
adjustTheHeap(arr, 0, i);
}
}
/**
* 最大堆:要求-父节点的值比其子节点大
* 调整堆:在调整之前我们对被调整的堆是有要求的,该堆必须是除了堆顶元素不符合(父大子小)要求外,其他节点都需符合要求
* 否则无法调整为最大堆
*
* 本函数的主要作用是将错误的堆顶元素移到正确的位置上
*
* @param arr 原数组
* @param index 节点索引,该节点为堆顶元素
* @param num 无序区间长度
*/
public void adjustTheHeap(int[] arr, int index, int num) {
int maxChild;
while ((maxChild = (index << 1) + 1) < num) { //当前节点的左子节点未超出无序区间(表示左子节点存在),进行如下操作
//若当前节点有左右两个子节点,先让它们先进行比较,选出较大的
if (maxChild + 1 < num && arr[maxChild] < arr[maxChild + 1]) {
maxChild++;
}
//当前节点与子节点进行比较,若当前节点大于其子节点,那么不用交换
if (arr[index] > arr[maxChild]) break;
//否则交换,此时父节点就是三者之间最大的那个值了
swap(arr, index, maxChild);
//交换后,原来的父节点,变成了子节点,我们选定该节点,重复以上的操作
index = maxChild;
}
}
/**
* 交换
*/
public void swap(int[] arr, int i, int j) {
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
- 注 意 : 注意: 注意:
- m a x = ( i n d e x < < 1 ) + 1 正 确 , m a x = i n d e x < < 1 + 1 错 误 , 优 先 级 问 题 max=(index << 1) + 1 正确,max=index << 1 + 1 错误,优先级问题 max=(index<<1)+1正确,max=index<<1+1错误,优先级问题
- 时 间 复 杂 度 : O ( n l o g n ) , 空 间 复 杂 度 : O ( 1 ) 时间复杂度:O(nlogn),空间复杂度:O(1) 时间复杂度:O(nlogn),空间复杂度:O(1)