堆排序首先满足堆的定义,堆可以看作完全二叉树的数组对象。
升序排序将数组整理成大根堆。
先定义堆化方法heapify,传入数组,根节点的索引,堆化元素的个数。
将根节点与其左右节点的较大值进行比较,若根节点元素较大,说明已经是大根堆,根节点较小,则将较大子节点的值赋值给他,将较大子节点的索引指向根节点索引,作为新的根节点,循环整理大根堆,直至根节点较大或者左子节点索引越界,退出循环,将开始的根节点值赋值给最终的根节点。
排序时,从倒数第二层的最后一个根节点进行堆化,向左向上方向,可以保证往后堆化时,下面的元素都是已经整理好的状态,当整棵树堆化完毕后,最大值位于根节点,将其与末尾值交换,即排序好了最大值的位置,将新的根节点在去除最大值后的二叉树内堆化,选出新的最大值,循环交换、堆化,完成排序。
1、堆排序首先满足堆的定义,完全二叉树(自上而下, 自左而右填充元素)
2、以升序为例,堆化为大顶堆
3、堆化程序
/**
* arr-------------待堆化的数组
* i ---------------开始堆化节点的索引
* length--------数组需堆化的长度,每进行1次排序(堆顶和堆尾元素交换),下次排序前就少堆化1个元素。
*/
public static void heapify(int[] arr, int i, int length){
int temp = arr[i];
//记录开始比较元素的数值
for(int k = 2 * i + 1; k < length; k = 2 * k + 1){
// 2i+1是比较元素的左子节点
// k<length 在数组长度内比较
// 2k+1是左子节点的左子节点,(越接近堆顶的堆化,越可能涉及比较子节点的子节点的大小,即用上2k+1的循环,第一次排序前,进行整棵树的堆化,由下→上,由右→左进行堆化)(这个顺序是根据数组索引来的)如图1,
图1 第一次排序前堆化顺序
if(k + 1 < length && arr[k] < arr[k + 1] ){
// 保证不越界的情况下,进行左子节点与右子节点的比较,找出较大的值
k++;
// 若满足,即右子节点大,将k++,取到右子节点索引
}
if(arr[k] > temp){
// 比较左、右子节点中较大的与temp的关系
arr[i] = arr[k];
// 若子节点数值大,则将待比较的初始位置元素替换为子节点数值
i = k;
// 未进行arr[k] = temp 原因,多次比较的话,若大一次就替换一次,会有较多的替换操作,i = k 直接定位到较大元素的索引,之后迭代,将最初始的temp,与子树下的节点比较,中间若有需要替换也可根据索引arr[i] = arr[k]进行替换了,因为第二次以及之后的比较前有i = k。 用相当于紫色代码前的arr[k]指向temp的值,原先k索引位置元素替换后被架空,不再考虑,让交换后的根节点的左右子树的值与temp对比,保证整个堆内全部元素进行对比成堆。
} else{
break;
// 若左、右子节点数值不如temp大,则不用比较啦,退出。原因:本次排序前,它的之前的堆已经排好,由下→上,由右→左进行堆化,见图1
}
}
//for循环结束后,即各个需要比较的子节点均与temp进行比较,最后一个大于temp的元素索引k赋值给了i,i=k,将temp值赋给它。
arr[i] = temp;
}
//交换数组元素
public static void swap(int[] arr, int a, int b){
int temp = arr[a];
arr[a] = arr[b];
arr[b] = temp;
}
//排序部分
public static void heapSort(int[] arr){
for(int i = (arr.length - 1 - 1) / 2 ; i >= 0; i--){
// 第一次排序前的堆化起始索引: arr.length - 1 - 1 /2
// i>=0 是由下→上,由右→左进行堆化,到达堆顶,对应数组索引为0
heapify(arr,i,arr.length);
}
//堆化完进行堆顶和堆尾元素交换(索引分别为0和arr.length - 1),之后再进行堆化交换,第一次排序后的堆化只有堆顶无序,其他的元素都按照之前的堆化排列完毕,所以heapify(int[] arr,int i,int length)中开始堆化的索引i=0,长度length也是每次递减,所以需要使用循环变量作为参数
for(int j = arr.length - 1; j > 0; j--){
swap(arr,0,j);
heapify(arr,0,j);
}
}
****----------------程序----------------****
import java.util.Arrays;
public class HeapSort {
public static void main(String[] args) {
int[] arr = {4,6,8,5,9,14,10,11,12};
System.out.println("堆排序前:");
System.out.println(Arrays.toString(arr));
heapSort(arr);
System.out.println("堆排序后:");
System.out.println(Arrays.toString(arr));
}
public static void heapify(int[] arr, int i, int length) {
int temp = arr[i];
for (int k = 2 * i + 1; k < length; k = 2 * k + 1) {
if (k + 1 < length && arr[k] < arr[k + 1]) {
k++;
}
if (arr[k] > temp) {
arr[i] = arr[k];
i = k;
} else {
break;
}
}
arr[i] = temp;
}
public static void swap(int[] arr, int a, int b) {
int temp = arr[a];
arr[a] = arr[b];
arr[b] = temp;
}
public static void heapSort(int[] arr) {
for (int i = (arr.length - 1 - 1) / 2; i >= 0; i--) {
heapify(arr, i, arr.length);
}
for (int j = arr.length - 1; j > 0; j--){
swap(arr,0,j);
heapify(arr,0,j);
}
}
}