前言
堆排序(Heapsort)是指利用堆这种数据结构所设计的一种排序算法。堆是一个近似完全二叉树的结构,并同时满足堆的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。堆排序可以说是一种利用堆的概念来排序的选择排序。分为两种方法:
大顶堆:每个节点的值都大于或等于其子节点的值,在堆排序算法中用于升序排列;
小顶堆:每个节点的值都小于或等于其子节点的值,在堆排序算法中用于降序排列;
算法分析
大顶堆流程
1、先从待排序的数组中构建一个大顶堆,堆的大小即数组的长度。
2、然后每次从大顶堆中取出堆顶元素,交换堆顶元素和最后一个元素的位置,再让堆的大小减1。
3、最后再从堆顶元素开始把当前数组再调整为一个大顶堆。
4、重复2、3步骤,最终完成排序。
动画演示
大顶堆排序
import java.util.Arrays;
public class MaxHeapSort {
static int[] arr = {2, 3, 1, 6, 4, 8, 1, 9};
static int[] res = new int[arr.length];
static int heapSize = 0;
public static void main(String[] args) {
buildMaxHeap();
System.out.println("大顶堆:" + Arrays.toString(arr));
adjustHeap();
System.out.println("排序后:" + Arrays.toString(res));
}
private static void adjustHeap() {
for (int i = 0; i < res.length; i++) {
//数组中第一个元素即 堆顶元素,所以肯定是当前最大的元素
res[i] = arr[0];
//让堆顶元素和最后一个节点的元素交换,并让当前堆的大小减1
swap(arr, 0, --heapSize);
//从堆顶开始重新调整堆结构(此时堆顶元素为最后一个节点的元素)
heapify();
}
}
/**
* 从堆顶元素开始,把当前数组调整为堆结构
* 以此比较当前元素的两个子节点,如果小于子节点就和子节点交换(和较大的那个子节点交换)
* 直到不小于子节点,或者超过heapSize范围为止。
*/
private static void heapify() {
//每次都是从第一个元素开始调整
int index = 0;
//左节点
int left = index * 2 + 1;
//left < heapSize表示还在堆结构的范围内
while (left < heapSize) {
//从左右两个子节点中找到较大值的那个节点
int largestPoint = left + 1 < heapSize && arr[left] < arr[left + 1] ? left + 1 : left;
//比较自己如果大于等于较大的那个子节点,就不用再向下交换了
if (arr[index] >= arr[largestPoint]) {
break;
}
//否则和较大的那个子节点交换位置
swap(arr, index, largestPoint);
//index来到较大的那个子节点的位置
index = largestPoint;
//找较大的那个子节点的左节点,继续遍历
left = index * 2 + 1;
}
}
/**
* 构建一个大顶推,初始化时heapSize(堆的大小)为0,每向堆中添加一个节点,heapSize就加1.
* 遍历结束后,整个数组就变为了大顶堆结构,并且heapSize就是数组的长度。
*/
private static void buildMaxHeap() {
for (int i = 0; i < arr.length; i++) {
heapInsert(i);
heapSize++;
}
}
/**
* 如果当前插入的节点,大于父节点,则当前节点与父节点交换,并把父节点指向当前节点,继续比较,直到不大于父节点为止
*
* @param index 当前节点
*/
private static void heapInsert(int index) {
while (arr[index] > arr[(index - 1) / 2]) {
swap(arr, index, (index - 1) / 2);
index = (index - 1) / 2;
}
}
private static void swap(int[] arr, int a, int b) {
int temp = arr[a];
arr[a] = arr[b];
arr[b] = temp;
}
}
小顶堆排序
import java.util.Arrays;
public class MinHeapSort {
public static void main(String[] args) {
int[] arr = {2, 4, 6, 1, 7, 3, 8, 7};
MinHeapSort c = new MinHeapSort();
System.out.println(Arrays.toString(c.sortArray(arr)));
}
public int[] sortArray(int[] nums) {
int[] res = new int[nums.length];
int heapSize = nums.length;
buildMinHeapSize(nums);
adjustMinHeap(nums, res, heapSize);
return res;
}
private static void adjustMinHeap(int[] arr, int[] res, int heapSize) {
for (int i = 0; i < res.length; i++) {
res[i] = arr[0];
swap(arr, 0, --heapSize);
heapify(arr, heapSize);
}
}
private static void heapify(int[] arr, int heapSize) {
int index = 0;
int left = 1;
while (left < heapSize) {
int less = left + 1 < heapSize && arr[left] > arr[left + 1] ? left + 1 : left;
if (arr[index] <= arr[less]) {
break;
}
swap(arr, index, less);
index = less;
left = index * 2 + 1;
}
}
private static void buildMinHeapSize(int[] arr) {
for (int i = 0; i < arr.length; i++) {
heapInsert(arr, i);
}
}
private static void heapInsert(int[] arr, int index) {
while (arr[index] < arr[(index - 1) / 2]) {
swap(arr, index, (index - 1) / 2);
index = (index - 1) / 2;
}
}
private static void swap(int[] arr, int a, int b) {
int temp = arr[a];
arr[a] = arr[b];
arr[b] = temp;
}
}