说到堆排序不得不说一下堆这种数据结构。这里的堆指二叉堆,本质是一个数组。可看作一个近似的完全二叉树(完全二叉树是所有叶子节点深度相同,且所有内部节点度为二的二叉树)。二叉堆有两种表现形式,最大堆和最小堆。最大堆中最大元素在二叉树的根结点,最小堆正好相反。我们的目的就是利用最大堆的特点,完成排序。
先上一段调试好的代码
package algorithms.portion6.heap;
/**
* desc : 堆排序
* Created by tiantian on 2018/7/24
*/
public class HeapSort {
public static int heapSize = 0;
// 维护最大堆
public static void maxHeapfy(Integer[] array, int i) {
int l = Node.left(i);
int r = Node.right(i);
int largest;
if (l <= heapSize && array[l] > array[i]) {
largest = l;
} else {
largest = i;
}
if (r <= heapSize && array[r] > array[largest]) {
largest = r;
}
if (largest != i) {
exchange(array, i, largest);
maxHeapfy(array, largest);
}
}
// 构建最大堆
public static void builtMaxHeap(Integer[] array) {
int i = (array.length)/2;
while (i >= 0) {
maxHeapfy(array, i);
i--;
}
}
// 排序
public static void sort(Integer[] array) {
heapSize = array.length-1;
builtMaxHeap(array);
int len = array.length;
for (int i = len-1; i >= 0 ; i--) {
exchange(array, 0, i);
heapSize = heapSize - 1;
maxHeapfy(array,0);
}
}
public static void exchange(Integer[] array, int a, int b) {
Integer temp = array[a];
array[a] = array[b];
array[b] = temp;
}
// 返回堆元素父节点,或者左右子节点。
public static class Node {
public static int parent(int i) {
return i/2;
}
public static int left(int i) {
return 2*i+1;
}
public static int right(int i) {
return 2 * i + 2;
}
}
}
客户端测试:
public class Client {
public static void main(String[] args) {
Integer[] testArray = {5,10,25,18,12,3,29,12,50,22,23,0,51,1,9,90,26,3,99,7};
// HeapSort.builtMaxHeap(testArray);
// for (Integer i : testArray) {
// System.out.print(i+",");
// }
System.out.println("排序以下数:");
for (int i = 0; i < testArray.length; i++) {
System.out.print(testArray[i] + ",");
}
System.out.println("");
System.out.println("排序结果:");
HeapSort.sort(testArray);
for (int i = 0; i < testArray.length; i++) {
System.out.print(testArray[i] + ",");
}
}
}
输出:
排序以下数:
5,10,25,18,12,3,29,12,50,22,23,0,51,1,9,90,26,3,99,7,
排序结果:
0,1,3,3,5,7,9,10,12,12,18,22,23,25,26,29,50,51,90,99,
这里有三个方法说明一下。第一个maxHeapfy方法接收一个数组和一个下标i,目的是使以下标为i的节点为根元素的堆变成最大堆。通过代码可以知道这一过程是不断递归调用maxHeapfy完成堆的最大化。exchange方法是交换两个数。builtMaxHeap方法是构建一个最大堆。这里heapSize变量一定要非常注意,它的作用是sort方法的循环的每次都使maxHeapfy最大化堆的范围不断缩小,避免弄乱已有序的元素。
最后总结下,堆排序是原址的或者说原地的,因为排好序的结果就是原来的数组。这里和之前的博文中讲过的归并排序比较下,归并排序的合并阶段是要借助另一数组辅助才能完成。堆排序时间复杂度为O(NlogN)。和归并一样是一种渐进最优的比较排序算法。