数据结构之二叉树(四):堆排序
基本介绍:
堆排序是一种选择排序,它的最坏,最好,平均实践复杂度均为O(nlogn),它也是不稳定排序。
堆介绍:
堆是一种有特殊性质的完全二叉树:
大顶堆:
每个节点的值都大于或者等于其左右子节点的值。
小顶堆:
每个节点的值都小于或者等于其左右子节点的值。
大顶堆示意图:
我们按堆中的节点按层进行编号,映射到数组中就是 arr[50,45,40,20,25,35,30,10,15];
即arr[i]>=arr[2i+1]&&arr[i]>=arr[2i+2];(i是对应的节点编号)
(注意:大小顶堆都没有要求左右子节点之间的大小关系)。
小顶堆示意图:
即arr[i]<=arr[2i+1]&&arr[i]<=arr[2i+2].
堆排序:
一般升序采用大顶堆,降序采用小顶堆。
堆排序基本思想:
1.先将待排序序列构造成一个大顶堆。
2.这时整个序列的最大值便是该大顶堆的根节点。
3.把这个根节点的值与待排序序列的末尾值进行交换,这个时候末尾就是最大值。
4.然后将剩下的n-1个树重新构造成一个堆,这样会得到一个n-1个元素中的最大值,再此与着n-1个元素的末尾元素进行交换。
5.如此反复执行,就能得到一个升序的有序序列。
图解实例:
首先我们先定义一个数组:
arr[4,5,8,5,9]
把它构造成一颗树如图:
按照思路,接下来要做的便是将其构造成一棵大顶堆。
将其构造成一棵大顶堆的思路如下:
1.首先我们先找到该树的最后一个非叶子节点(arr.length/2-1可得)(也就是节点6),让6节点与其左右子节点比较,取三者中的最大值与节点6进行交换。
交换结果如下:
于是树相对应的数组便变成了
arr[4,9,8,5,6]
2.接着从左至右,从下至上进行调整
再找到第二个非叶子节点(也就是4节点)同样进行比较交换,得到如下:
3.此时发现替换后的节点4仍比它的左右子节点小,再次交换,最后得到如下:
此时得到数组
arr[9,6,8,5,4]
接着将root节点(9节点)与末尾进行交换,得到如下:
最后以此类推,不断进行比较,交换,置后,最后便得到了一个升序数组。
java参考代码如下:
import java.util.Arrays;
public class HeapSort {
public static void main(String[] args) {
//升序排列
int arr[]= {
4,6,8,5,9};
heapSort(arr);
System.out.println(Arrays.toString(arr));
}
//堆排序方法
public static void heapSort(int[] arr) {
int temp=0;
for(int i=arr.length/2-1;i>=0;i--) {
makeBigHeap(arr,i,arr.length);
}
for(int j=arr.length-1;j>0;j--) {
temp=arr[j];
arr[j]=arr[0];
arr[0]=temp;
makeBigHeap(arr,0,j);
}
}
/*
* 将数组调整成大顶堆的方法:将以i对应的非叶子节点树调整成大顶堆
* arr:待调整数组
* i:非叶子节点在数组中索引
* length:调整数组的长度
*/
public static void makeBigHeap(int arr[],int i,int length) {
int temp=arr[i];//取出当前元素,保存在临时变量
//k=i*2+1,k是i节点的左子节点
for(int k=i*2+1;k<length;k=k*2+1) {
if(k+1<length&&arr[k]<arr[k+1]) {
k++;//k指向右子节点
}
if(arr[k]>temp) {
//如果子节点大于父节点
arr[i]=arr[k];//把较大的值赋值给当前节点
i=k;//继续循环比较
}else {
break;
}
}
arr[i]=temp;//将temp赋值放到调整后的位置
}
}