堆排序
时间复杂度 O(nlogN)
大顶堆:
大顶堆是一个完全二叉树,其左右子节点小于或等于根节点,左右子节点之间并无关系。
由此可知大顶堆的根节点为最大值。
例:
int[] a = {3,4,2,7,1,6,5,0};
其构建的初始堆为
3 4 2 7 1 6 5 0
调整为大顶堆:
7 4 6 3 1 2 5 0
了解了大顶堆后,再来看堆排序的步骤:
- 将初始待排序关键字序列(R1,R2….Rn)构建成大顶堆,此堆为初始的无序区;
- 将堆顶元素R[1]与最后一个元素R[n]交换,此时得到新的无序区(R1,R2,……Rn-1)和新的有序区(Rn),且满足R[1,2…n-1]<=R[n];
- 由于交换后新的堆顶R[1]可能违反堆的性质,因此需要对当前无序区(R1,R2,……Rn-1)调整为新堆,然后再次将R[1]与无序区最后一个元素交换,得到新的无序区(R1,R2….Rn-2)和新的有序区(Rn-1,Rn)。不断重复此过程直到有序区的元素个数为n-1,则整个排序过程完成。
在上图中,就是将0与7调换,然后a[7] = 7;a[0] = 0;很显然此时不满足大顶堆性质,于是对a[0]~a[6]进行堆调整,调整后6为根节点,再将a[6]与a[0]交换,此时a[6]=6,a[7] = 7;可见是有序的,重复该步骤即可。
详细实现见源码
java实现:
public class HeapSort {
public static void main(String[] args){
int[] a = {3,4,2,7,1,6,5,0};
heapSort(a);
}
/**
* 堆大小
*/
private static int len;
/**
* 堆排序
* @param arr
* @return
*/
public static int[] heapSort(int[] arr) {
// 构建大顶堆
buildMaxHeap(arr);
for (int i = arr.length - 1; i > 0; i--) {
// 首尾元素交换,这样arr[length-1]就是最大值
swap(arr, 0, i);
// 减少下次堆调整的大小,忽略上一条语句交换得到的最大值,此时数组从后面逐渐变成有序的
len--;
// 因为根节点变动,所以需要重新调整
heapify(arr, 0);
}
return arr;
}
/**
* 给定一个数组构造大顶堆
* @param arr
*/
private static void buildMaxHeap(int[] arr) {
System.out.println("buildMaxHeap start");
len = arr.length;
for (int i = len/2; i >= 0; i--) {
heapify(arr, i);
}
System.out.println("buildMaxHeap end");
}
/**
* 堆调整
* @param arr 待调整的元素
* @param i 调整第i位节点及其左右节点,根节点 i = 0
*/
private static void heapify(int[] arr,int i) { // 堆调整
print(arr);
int left = 2 * i + 1, // // i节点的左子结点
right = 2 * i + 2, // i节点的右子结点
largest = i; // 记录这三个节点最大的那个
// 如果第i节点的左节点存在且左节点比它大,记录largest
if (left < len && arr[left] > arr[largest]) {
largest = left;
}
// 如果第i节点的右节点存在且比arr[largest]大,记录largest
// 注意largest既可能为根节点i,也可能为i的左节点,总之这三个节点要保证堆的性质,找到这三个节点最大的那个置于根节点,记录largest
if (right < len && arr[right] > arr[largest]) {
largest = right;
}
if (largest != i) {
// 如果largest != i,则说明节点i小于其左右节点,因此需要交换位置,保证根节点最大
swap(arr, i, largest);
// 因为此时堆发生了变动,所以需要重建交换的子节点,保证该节点保证堆性质
heapify(arr, largest);
}
}
/**
* 数组交换
* @param arr
* @param i 数组下标
* @param j 数组下标
*/
private static void swap(int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
/**
* 数组输出
* @param array
*/
private static void print(int[] array){
for(int a : array){
System.out.print(a+" ");
}
System.out.print("
");
}
}
输出:
buildMaxHeap start
3 4 2 7 1 6 5 0
3 4 2 7 1 6 5 0
3 4 2 7 1 6 5 0
3 4 6 7 1 2 5 0
3 4 6 7 1 2 5 0
3 7 6 4 1 2 5 0
3 7 6 4 1 2 5 0
7 3 6 4 1 2 5 0
7 4 6 3 1 2 5 0
buildMaxHeap end
0 4 6 3 1 2 5 7
6 4 0 3 1 2 5 7
6 4 5 3 1 2 0 7
0 4 5 3 1 2 6 7
5 4 0 3 1 2 6 7
5 4 2 3 1 0 6 7
0 4 2 3 1 5 6 7
4 0 2 3 1 5 6 7
4 3 2 0 1 5 6 7
1 3 2 0 4 5 6 7
3 1 2 0 4 5 6 7
0 1 2 3 4 5 6 7
2 1 0 3 4 5 6 7
0 1 2 3 4 5 6 7
1 0 2 3 4 5 6 7
0 1 2 3 4 5 6 7
Process finished with exit code 0