前置知识
堆排序主要利用的是大顶堆的思想(父节点大于等于子节点),在数组中可以用二叉树的表示为第n个元素的左子节点为2*n+1,右子节点为2*n+2,父节点为(n-1)/2。即arr[i]>=arr[2*i+1]&&arr[i]>=arr[2*i+2]。
堆排序的基本思想
1.将排序数组构造为一个大顶堆
2.此时,整个序列的最大值就是堆顶的根节点
3.将其与末尾元素进行交换,此时末尾就是最大值
4.然后将剩余的n-1个元素重新构造成一个堆,重复1,2,3,步最后得到一个有序序列。
图解如下
步骤一:构造大顶堆
1.原始数组为arr=[4,6,8,5,9]
2.从最后一个非叶子节点进行调整,即i=(arr.length/2)-1=1,从0到i都是父节点,从左至右,从下到上依次进行调整。
3.找到第二个非叶子节点arr[0]=4,进行调整。
4.调整完发现[4,5,6]不是一个大顶堆,继续调整
步骤二:将堆顶元素与末尾元素进行交换,再继续调整堆,交换,调整,交换......
1.将堆顶元素9与末尾元素4交换
2.继续调整堆结构 3.将元素8与5进行调换。
4.继续调整,交换直到数组有序。
代码如下
1.构建大顶堆
public static void adjust(int[] arr,int index,int len){
int temp=arr[index];
for(int i=2*index+1;i<len;i=2*i+1){
//比较两个叶子节点的值,让i指向最大的值
if(i+1<len&&arr[i]<arr[i+1]){
i++;
}
// 如果叶子节点的值大于父节点,将叶子节点的值赋给父节点,同时该叶子节点
// 成为下一个父节点。
if(arr[i]>temp){
arr[index]=arr[i];
index=i;
}else {
break;
}
}
//最后将最开始的存储的父节点赋给叶子节点
arr[index]=temp;
}
2.首先构造一个大顶堆,在进行首尾交换,继续构造大顶堆。
public static void sort(int[] arr){
for(int i=arr.length/2-1;i>=0;i--){
adjust(arr,i,arr.length);
}
for(int i=arr.length-1;i>=0;i--){
int temp=arr[i];
arr[i]=arr[0];
arr[0]=temp;
adjust(arr,0,i);
}
}
完整代码
import java.util.Arrays;
public class test {
public static void main(String[] args) {
int[] arr={4,6,1,2,9,8,3,5,-1,-100,0,9} ;
sort(arr);
System.out.println(Arrays.toString(arr));
}
public static void sort(int[] arr){
for(int i=arr.length/2-1;i>=0;i--){
adjust(arr,i,arr.length);
}
for(int i=arr.length-1;i>=0;i--){
int temp=arr[i];
arr[i]=arr[0];
arr[0]=temp;
adjust(arr,0,i);
}
}
public static void adjust(int[] arr,int index,int len){
int temp=arr[index];
for(int i=2*index+1;i<len;i=2*i+1){
//比较两个叶子节点的值,让i指向最大的值
if(i+1<len&&arr[i]<arr[i+1]){
i++;
}
// 如果叶子节点的值大于父节点,将叶子节点的值赋给父节点,同时该叶子节点
// 成为下一个父节点。
if(arr[i]>temp){
arr[index]=arr[i];
index=i;
}else {
break;
}
}
//最后将最开始的存储的父节点赋给叶子节点
arr[index]=temp;
}
}