堆的定义:
堆其实是一棵二叉树,它分为最大堆和最小堆
最大堆:父结点不小于左右子结点
最小堆:父结点不大于左右子结点
堆排序就是利用最大堆或者最小堆来排序,由于二者原理相似,本文只讨论最大堆排序。
堆排序的时间复杂度:O(nlgn)。空间复杂度:原地排序,不用额外空间。
数组和二叉树可以相互转换,如下图:
对于数组中的每一个数据index i,它的左结点等于index为2 * i + 1的元素,右结点等于index为2 * (i + 1)的元素。
package com.wind.sort;
public class HeapSort {
public int[] sort(int[] arr) {
// 创建最大堆
buildMaxHeapTree(arr);
// now the tree is max heap tree
for(int i = 0; i < arr.length; i++) {
// 最大堆之后,root节点即为最大值,将它与数组中最后一个元素互换,
// 然后前n-1个元素可以看成是一个新的最大堆,但是root节点的值不对,
// 所以我们只要对root节点在作一次最大堆转换
swap(arr, 0, arr.length - i - 1);
maxHeap(arr, 0, arr.length - i - 1);
}
// now arr is sort from min to max
return arr;
}
private void buildMaxHeapTree(int[] arr) {
// 这儿为什么是arr.length / 2呢?因为对于叶结点,它没有左右子结点,所以本身就是最大点,
// 所以我们只要从最后一个非叶结点开始就行,当然从arr.length - 1开始也没有问题
for(int i = arr.length / 2; i >= 0; i--) {
maxHeap(arr, i, arr.length);
}
}
private void maxHeap(int[] arr, int i, int treeLength) {
int l = left(i);
int r = right(i);
int max = i;
// parent node is the max of parent node, left node and right node,
if(l < treeLength && arr[l] > arr[max]) {
max = l;
}
if(r < treeLength && arr[r] > arr[max]) {
max = r;
}
// max node is not parent node, change it, after that, the changed
// child node should max heap again
if(max != i) {
swap(arr, i, max);
maxHeap(arr, max, treeLength);
}
}
/**
* Get tree left node.
*/
private int left(int i) {
return 2 * i + 1;
}
/**
* Get tree right node.
*/
private int right(int i) {
return 2 * (i + 1);
}
private void swap(int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}