堆,是一种类似于二叉树的数据结构。
它分为大顶堆和小顶堆,以大顶堆为例,大顶堆的每一个父节点一定大于它的左右子节点;小顶堆顾名思义。
大致步骤
所以,利用这种性质,便可以实现数组的排序。步骤如下:
- 以大顶堆为例(通常用于升序排列),先将数组的元素构成一个大顶堆;
- 取出堆顶元素,一定是最大的,然后放在数组末尾(交换数组末尾元素);
- 剩下的元素重新组成一个大顶堆,然后再次取出堆顶元素,放在上一个的前面;
- 以此类推,直到堆中无元素,数组也就排好序了。
构建大顶堆
以上就是堆排序的大致过程,那么如何构建一个大顶堆呢?具体实现如下:
- 从后向前扫描所有的非叶子节点(叶子节点一定符合要求),判断是否符合大顶堆要求,不符合则与子节点交换。
- 注意,如果交换完成后,子节点不符合大顶堆要求了,那么就重新将该子节点转换为大顶堆。
- 还有,假如数组长length,最后一个非叶子节点的下标为
(length-2)/2
。 - 假如节点下标为i,则左子节点下标为
2*i+1
,右子节点下标为2*i+2
。
代码
public class HeapSort {
/**
* 用于每一次构造一个大顶堆
* @param arr 数组
* @param length 当前大顶堆所对应长度
*/
public void heapSort(int[] arr,int length){
for(int i=(length-2)/2;i>=0;i--){
heapfy(arr,i,length);
}
}
/**
* 用于将每一个节点大顶堆化
* @param arr 数组
* @param root 当前节点
* @param length 当前大顶堆对应长度
*/
private void heapfy(int[] arr, int root, int length) {
int left = root*2 + 1; //左子节点的下标
int right = root*2 + 2; //右子节点的下标
int max_index = left; //表示左右子节点较大的那个下标,默认为left,一会儿交换的就是这个
if(right<length){ //当存在右子节点时,取最大的
max_index = arr[left]>arr[right]?left:right;
}
if(arr[root]<arr[max_index]){ //假如不符合大顶堆要求
int temp = arr[root];
arr[root] = arr[max_index];
arr[max_index] = temp; //交换两个值
if(max_index<=(length-2)/2){ //当子节点不是叶子节点时,才需要对子节点堆化
heapfy(arr,max_index,length); //为防止交换后子节点不符合大顶堆了,就重新对它大顶堆化
}
}
}
public static void main(String[] args) {
int[] arr = {2,3,5,1,6,4};
HeapSort heapSort = new HeapSort();
for(int i=arr.length-1;i>0;i--){ //i从数组后向前遍历,负责控制每次堆排序的长度
heapSort.heapSort(arr,i+1);
int temp = arr[0];
arr[0] = arr[i];
arr[i] = temp; //交换头尾元素
}
}
}