1. 前置知识
a. 什么是堆
是一种特殊的完全二叉树结构,分为大根堆和小根堆两种,以大根堆为例,即要保证父亲节点比其左右节点大。
b. 堆的插入(以大根堆为例)
以数组实现的完全二叉树,通过左孩子的下标是父亲下标乘二加一的位置,右孩子在乘二加二的位置。左右孩子任意一个位置减一除二就能得到父亲的位置。
将每个要插入的数放在堆的最后,也就是数组的最后,然后让该位置与其父亲位置进行比较若是大于父亲节点则交换,然后该位置更新到其父亲节点的位置,再与现在的父亲节点比较直到小于其父亲节点或者已经换到0位置结束。示例代码在最后
c. 堆的删除(删除堆中的最大元素)
将堆的最后一个元素和第一个元素交换,然后让堆的大小减一(就删除了最大元素),然后队堆进行向下调整,对于每个节点找到其左右孩子中最大的那个若是比自己大则进行交换,自己来到最大孩子的位置,如此循环直到自身比左右孩子中最大那个大或者已经没有左右孩子。示例代码在最后
2.核心思路
将要排序的数组中的元素一个一个的进行堆的插入操作,让该数组成为一个堆,此过程为NlogN,让后将最后一个与第一个交换,此时最大的已经排在了最后,然后缩小堆(就是堆的删除的操作)对剩下的堆进行向下调整操作,之后再进行交换如此循环,直到堆还剩一个元素。
3.示例代码
public static void heapSort(int arr[]){
//建堆
for(int i = 0; i < arr.length; i++){
Code02_Heap.heepInsert(arr, i);
}
/*
第一个元素和最后一个交换,然后将堆收缩一个在进行(此时的最大值已经在最右端了)
heapify将剩下改变后的堆调好后(就是类似于pop的过程)再交换,
*/
for(int i = 0; i < arr.length - 1; i++){
int last = arr.length - 1 - i;
swap(arr, 0, last);
Code02_Heap.heapify(arr, 0, last);
}
}
/*
对于新插入的节点,先将其放在堆的末尾
然后向上去和其的父节点比较,若大于父节点则
交换,交换后再与其父节点比较直到比父节点小
或者交换到根节点后跳出循环。
*/
public static void heepInsert(int[] heep, int size){
while(heep[size] > heep[(size - 1) / 2]){
swap(heep, size, (size - 1) / 2);
size = (size - 1) / 2;
}
// while((pos - 1) / 2 >= 0){
// int p = (pos - 1) / 2;
// if(heep[pos] > heep[p]){
// swap(heep, pos, p);
// pos = p;
// }else {
// break;
// }
// }
}
private static void heapify(int heep[], int index, int size){
int left = 2 * index + 1;
while(left < size){
//找最大的那个孩子,若是右孩子存在且大于左孩子则是右,否则是左
int largest = left + 1 < size && heep[left + 1] > heep[left] ? left + 1 : left;
//判断父节点与最大孩子节点的大小
largest = heep[largest] > heep[index] ? largest : index;
if(largest == index){
break;
}
swap(heep, largest, index);
index = largest;
left = 2 * index + 1;
}
}