算法描述:
堆在逻辑上是一个完全二叉树,而在物理上其实是一个数组/向量。若非叶子结点的坐标为i,则其左孩子结点的坐标为(2i+1),其右孩子结点的坐标为(2i+2)。
① 将待排序的序列构造成一个大顶堆,根据大顶堆的性质,当前堆的根节点(堆顶)就是序列中最大的元素;
② 将堆顶元素和最后一个元素交换,然后将剩下的节点重新构造成一个大顶堆;
③ 重复步骤2,如此反复,从第一次构建大顶堆开始,每一次构建,我们都能获得一个序列的最大值,然后把它放到大顶堆的尾部。最后,就得到一个有序的序列了。
对于大顶堆:arr[i] >= arr[2i + 1] && arr[i] >= arr[2i + 2]
对于小顶堆:arr[i] <= arr[2i + 1] && arr[i] <= arr[2i + 2]
package com.lin;
public class HeapSort {
public static void main(String[] args) {
System.out.println("=====堆排序=====");
int[] array = new int[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
System.out.println("排序之前:");
for (int i = 0; i < array.length; i++) {
System.out.print(array[i] + " ");
}
//堆排序
array = heapSort(array);
System.out.println("\n");
System.out.println("排序之后:");
for (int i = 0; i < array.length; i++) {
System.out.print(array[i] + " ");
}
}
public static int[] heapSort(int[] arr) {
if (arr == null || arr.length == 0) {
return arr;
}
int len = arr.length;
// 构建大顶堆,这里其实就是把待排序序列,变成一个大顶堆结构的数组
buildMaxHeap(arr, len);
// 交换堆顶和当前末尾的节点,重置大顶堆
for (int i = len - 1; i > 0; i--) {
swap(arr, 0, i);
len--;
heapify(arr, 0, len);
}
return arr;
}
private static void buildMaxHeap(int[] arr, int len) {
// 从最后一个非叶节点开始向前遍历,调整节点性质,使之成为大顶堆
for (int i = len / 2 - 1; i >= 0; i--) {
heapify(arr, i, len);
}
}
private static void heapify(int[] arr, int i, int len) {
// 先根据堆性质,找出它左右节点的索引
int left = 2 * i + 1;
int right = 2 * i + 2;
// 默认当前节点(父节点)是最大值。
int largestIndex = i;
if (left < len && arr[left] > arr[largestIndex]) {
// 如果有左节点,并且左节点的值更大,更新最大值的索引
largestIndex = left;
}
if (right < len && arr[right] > arr[largestIndex]) {
// 如果有右节点,并且右节点的值更大,更新最大值的索引
largestIndex = right;
}
if (largestIndex != i) {
// 如果最大值不是当前非叶子节点的值,那么就把当前节点和最大值的子节点值互换
swap(arr, i, largestIndex);
// 因为互换之后,子节点的值变了,如果该子节点也有自己的子节点,仍需要再次调整。
heapify(arr, largestIndex, len);
}
}
private static void swap(int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
运行结果