堆排序是著名的原地且不稳定的排序算法之一。顾名思义,堆排序是利用数据结构-堆(Heap)。
堆有两个特点:
- 堆是一个完全二叉树(完全二叉树要求除了最后一层,其他层的节点都是满的,最后一层的节点都靠左排列。)
- 堆中每个节点都必须大于等于(或小于等于)其子树中每个节点的值。
堆的时间复杂度:
最好, 最坏,平均都是O(nlogn)。
堆分为两类:
- 大顶堆, 顶点为最大值。
- 小顶堆, 顶点为最小值。
public class HeapSort {
// 堆排序是原地且不稳定的算法,不稳定的原因是需要把叶子节点和堆顶节点进行交换
// 最好,最坏,平均时间复杂度都是O(nlogn)
// 堆的定义:堆是一个完全二叉树(完全二叉树要求除了最后一层,其他层的节点都是满的,最后一层的节点都靠左排列。),堆中每个节点都必须大于等于(或小于等于)其子树中每个节点的值。
// 堆排序有两种类型:大顶堆,小顶堆
// 大顶堆:节点的大于子节点, 小顶堆:节点小于子节点
// 利用: 求Top K问题, 求中位数问题
// 堆的构建:从数组下标1开始使用,即堆顶元素的下标为0, 一个节点的下标为i,其子节点为2*i+1和2*i+2,其父节点为(i-1)/2
/**
* 利用小顶堆来从小到大的排序
*
* @param data 数组
* @param n 数组的长度
* @return
*/
public static void minHeapSort(int[] data, int n) {
if (n <= 1) return;
// 建堆
buildMinHeap(data);
// 排序
int k = data.length - 1;
while (k > 0) {
// 将堆顶元素(最大)与最后一个元素交换位置
swap(data, 0, k);
// 将剩下元素重新堆化,堆顶元素变成最大元素
heapifyInMinHeap(data, --k, 0);
}
}
/**
* 利用大顶堆来从大到小的排序
*
* @param data 数组
* @param n 数组的长度
* @return
*/
public static void maxHeapSort(int[] data, int n){
if(n<=1) return;
// 建堆
buildMaxHeap(data);
// 排序
int k = data.length - 1;
while (k > 0) {
// 将堆顶元素(最小)与最后一个元素交换位置
swap(data, 0, k);
// 将剩下元素重新堆化,堆顶元素变成最大元素
heapifyInMaxHeap(data, --k, 0);
}
}
/**
* 建小顶堆
*
* @param data 数组
*/
private static void buildMinHeap(int[] data) {
// (arr.length) / 2 为最后一个叶子节点的父节点
// 也就是最后一个非叶子节点,依次堆化直到根节点
// 这里i>=1的原因是,堆顶是从下标1开始储存
for (int i = (data.length - 1) / 2; i >= 0; i--) {
heapifyInMinHeap(data, data.length - 1, i);
}
}
/**
* 建大顶堆
*
* @param data 数组
*/
private static void buildMaxHeap(int[] data) {
for (int i = (data.length - 1) / 2; i >= 0; i--) {
heapifyInMaxHeap(data, data.length - 1, i);
}
}
/**
* 小顶堆的堆化,为了删除或者添加数据时使数据结构满足堆的要求
*
* @param data 数组
* @param n 数组的长度
* @param i 要堆化的下标
* @return
*/
private static void heapifyInMinHeap(int[] data, int n, int i) {
while (true) {
int maxPos = i;
if (2 * i + 1 < n && data[i] < data[2 * i + 1]) maxPos = 2 * i + 1;
if (2 * i + 2 < n && data[maxPos] < data[2 * i + 2]) maxPos = 2 * i + 2;
if (maxPos == i) break;
swap(data, i, maxPos);
i = maxPos;
}
}
/**
* 大顶堆的堆化,为了删除或者添加数据时使数据结构满足堆的要求
*
* @param data 数组
* @param n 数组的长度
* @param i 要堆化的下标
* @return
*/
private static void heapifyInMaxHeap(int[] data, int n, int i) {
while (true) {
int minPos = i;
if (2 * i + 1 < n && data[i] > data[2 * i + 1]) minPos = 2 * i + 1;
if (2 * i + 2 < n && data[minPos] > data[2 * i + 2]) minPos = 2 * i + 2;
if (minPos == i) break;
swap(data, i, minPos);
i = minPos;
}
}
/**
* 数组中数据的交换
*
* @param data 数组
* @param left 要交换的下标
* @param right 要交换的下标
*/
private static void swap(int[] data, int left, int right) {
int tmp = data[left];
data[left] = data[right];
data[right] = tmp;
}
}