本文介绍一下常见的堆排序的原理及实现代码。
简介
堆排序是一种树形选择排序方法,要了解堆排序就不得不了解一下堆。简单的说,堆是一种完全二叉树,根据父子节点之间的大小关系的不同还可以细分为大顶堆和小顶堆。大顶堆是指任一节点的值都大于或等于其左右孩子的值,小顶堆是指任一节点的值都小于或等于其左右孩子的值。下图分别是大顶堆和小顶堆的结构。
堆排序过程
我们以大顶堆为例,演示一下堆排序过程。
现在我们有一个待排序数组[2,7,4,3,1,9,5],初始状态是这样的:
进行堆排序时,一般需要对节点从下到上,从右到左进行调整,我们先调整右下角的部分。
右下角的部分就调整好了,我们看左下角是符合父节点大于孩子节点的,所以跳过看上一层的。
我们交换了2和9之后要注意被换下来的2是比4和5小的,所以要把5和2再交换一下。
这下就是一个大顶堆了,根节点就是我们要找的最大值,我们把根节点和右下角的节点互换位置,做一下“沉淀”。注意,此时我们已经将最大的节点放到最后了。
接下来我们只需要进行适当的调整大顶堆就可以了。我们注意到此时的根节点是相当比较小的,只需要把左右孩子中较大的和它交换位置,然后重复这个过程就可以了。
做完上面的步骤我们发现根节点的值又是当前堆的最大元素了,我们把根节点7和最后一个节点交换,7成了倒数第二个节点,重复上面的过程,就能得到一个排好序的数组了。
Java代码实现
public class HeapSort {
public static void heapSort(int[] arr) {
//1.将无序序列构建为一个大顶堆
for (int i = arr.length / 2 - 1; i >= 0; i--) {
adjustHeap(arr, i, arr.length);
}
//2.将堆顶元素与末尾元素交换,最大元素沉淀到末尾
//3.重复这个过程
for (int j = arr.length - 1; j > 0; j--) {
int temp = arr[j];
arr[j] = arr[0];
arr[0] = temp;
adjustHeap(arr, 0, j);
}
}
//将一个数组(二叉树),调整为一个大顶堆
public static void adjustHeap(int arr[], int i, int length) {
int temp = arr[i];//先取出当前元素的值,保存在临时变量
//开始调整
//说明:
//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;//i指向k,继续循环比较
} else {
break;
}
}
//当for循环结束后,已经将以i为父结点的树的最大值放在了最顶(局部)
arr[i] = temp;//将temp值放到调整后的位置
}
public static void main(String[] args) {
int[] arr = {2,7,4,3,1,9,5};
heapSort(arr);
System.out.println(Arrays.toString(arr));
}
}