什么是堆排序 (Heapsort)
堆排序(英语:Heapsort)是指利用堆这种数据结构所设计的一种排序算法。堆是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。
注意:对于堆的讲解,在另外一篇文章中有讲解,数据结构-堆.
堆排序的步骤
- 交换函数
交换函数:因为在构建堆和排序的过程中,需要多次交换元素的位置,所以写了一个交换数组两个位置的函数,在排序算法中,这个函数还是比较重要的,很多排序算法都有用到。
/**
* 交换两个位置的数据
* @param arr
* @param i
* @param j
*/
public static void swap(int[] arr,int i ,int j){
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
- 堆化函数
堆化函数:主要就是针对于每一个子树,把根节点的值和两个子节点的值做比较,把最大的一个交换到根节点的位置,如果有交换,则需要处理以被交换位置的子节点作为根节点的子树(此处选用的是递归)。
/**
* //堆化
* @param arr
* @param len
*/
public static void heapify(int[] arr,int index,int len){
int left = 2 * index + 1;
int right = 2 * index + 2;
int max = index;
//左边的大,交换左边的
if(left < len && arr[left] > arr[max]){
max = left;
}
//右节点,与左节点和根节点中较大的比较
if(right < len && arr[right] > arr[max]){
max = right;
}
//如果不相等,说明index位置不是最大的
if(max != index){
//左右子节点,与根节点交换
swap(arr,index,max);
//由于当前位置发生了调整,所以继续调整当前节点和其子节点
heapify(arr,max,len);
}
}
- 构建堆
构建堆:就是把一个无序的数组,初步构建成一个大根堆,从数组的1/2的位置开始,为什么选用这个位置呢,是因为堆的性质:ki >= k2i +1 且 ki >= k2i + 2,所以最后一个元素的父节点,在(len - 1)/2位置。所以从这里开始,就可以覆盖所有的元素。
//构建堆
public static void buildHeap(int [] arr,int len){
//这里从数组长度的一半位置,开始向前堆化
for(int i = (len - 1)/2; i >= 0;i--){
heapify(arr,i,len);
}
}
-
堆排序
堆排序:循环整个数组,利用堆的性质进行排序,把构建好的大根堆(堆顶元素最大)的根位置与最后一元素交换,然后继续把剩余的元素构建成一个大根堆(堆顶元素最大),继续把堆顶的元素交换的倒数第二个位置.....以此类推,就把数组调整成了一个有序数组。
//堆排序 for (int i = arr.length-1;i >= 0;i--){ swap(arr,0,i); heapify(arr,0,i); }
结论
堆排序就是利用堆的堆顶位置是最小或者最大的性质,把构建好的大根堆或者小根堆,堆顶元素和后面位置的元素交换,以此把最大或者最小的元素交换到数组后方, 从而达到排序的效果。
注意:升序排序,需要构建大根堆,降序排序,需要构建小根堆。
完整的代码
import java.util.Arrays;
public class HeapSort {
public static void main(String[] args) {
int[] arr = {3,2,4,5,6,7,5,1,2,3,9,7};
//构建堆
buildHeap(arr,arr.length);
//堆排序
for (int i = arr.length-1;i >= 0;i--){
swap(arr,0,i);
heapify(arr,0,i);
}
// 打印排序后的数组
System.out.println(Arrays.toString(arr));
}
//构建堆
public static void buildHeap(int [] arr,int len){
//这里从数组长度的一半位置,开始向前堆化
for(int i = (len - 1)/2; i >= 0;i--){
heapify(arr,i,len);
}
}
/**
* //堆化
* @param arr
* @param len
*/
public static void heapify(int[] arr,int index,int len){
int left = 2 * index + 1;
int right = 2 * index + 2;
int max = index;
//左边的大,交换左边的
if(left < len && arr[left] > arr[max]){
max = left;
}
//右节点,与左节点和根节点中较大的比较
if(right < len && arr[right] > arr[max]){
max = right;
}
//如果不相等,说明index位置不是最大的
if(Largest != index){
//左右子节点,与根节点交换
swap(arr,index,max);
//由于当前位置发生了调整,所以继续调整当前节点和其子节点
heapify(arr,max,len);
}
}
/**
* 交换两个位置的数据
* @param arr
* @param i
* @param j
*/
public static void swap(int[] arr,int i ,int j){
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
输出结果
输入:{3,2,4,5,6,7,5,1,2,3,9,7}
输出:[1, 2, 2, 3, 3, 4, 5, 5, 6, 7, 7, 9]