堆排序一种时间复杂度为 O ( n log n ) O(n\log n) O(nlogn)的原址排序,其实现可以只利用常数级的额外空间。其详细介绍可参考《算法导论》中的介绍。本文主要记录堆排序的Java实现过程。
Heap抽象类
一般有两种常见的堆的实现方式:最大堆和最小堆。其区别在与根节点是比子节点大还是小。若根节点不小于子节点,则为最大堆,若根节点不大于子节点,则为最小堆。这两种堆的实现方式的区别在与建堆的过程,其他的地方基本一致,因此为了提高代码的重复利用率,可以先构造一个抽象类Heap
,一些重复的方法可以直接在Heap中实现,而建堆时需要调用的关键函数heapify(int i)
可以设置成抽象方法,最后在MaxHeap
类和MinHeap
类中实现。为了应对多种数据类型的需要,这里实现了泛型的版本。
public abstract class Heap<T extends Comparable<T>> {
public int heapSize;
public T[] list; // 保存元素的数组
public Heap() {
heapSize = 0;
}
// 建堆所需要的子函数
// 根据是最大堆还是最小堆,分别有两种实现方式
public abstract void heapify(int i);
// 建堆
public void buildHeap(T[] list) {
heapSize = list.length;
this.list = list;
for(int i=(heapSize-1)/2; i>=0; i--) {
heapify(i);
}
}
public int parentIndex(int i) {
return (i-1)/2;
}
public int leftChildIndex(int i) {
return 2*i+1;
}
public int rightChildIndex(int i) {
return 2*i+2;
}
public String toString() {
String str = " ";
for(int i=0; i<heapSize; i++) {
str = str + list[i] + " ";
}
return str;
}
}
最大堆实现
通过继承Heap
类,这里只需要把heapify(int i)
方法实现一下就可以了。
public class MaxHeap<T extends Comparable<T>> extends Heap<T>{
public MaxHeap() {
super();
}
@Override
public void heapify(int i) {
// TODO Auto-generated method stub
int left = leftChildIndex(i);
int right = rightChildIndex(i);
int max = i;
if(left<heapSize && list[left].compareTo(list[i])>0) {
max = left;
}
if(right<heapSize && list[right].compareTo(list[max])>0) {
max = right;
}
if(max != i) {
T temp = list[i];
list[i] = list[max];
list[max] = temp;
heapify(max);
}
}
public static void main(String[] args) {
Heap<Integer> heap = new MaxHeap<>();
Integer[] nums = {4, 1, 3, 2, 16, 9, 10, 14, 8, 7};
heap.buildHeap(nums);
System.out.println(heap);
}
}
在main
方法中,加了一个测试用例
4, 1, 3, 2, 16, 9, 10, 14, 8, 7
其测试输出为
16 14 10 8 7 9 3 2 4 1
画出这个堆的结构如下
最小堆实现
与最大堆类似,其实现代码如下
public class MinHeap<T extends Comparable<T>> extends Heap<T> {
public MinHeap() {
super();
}
@Override
public void heapify(int i) {
// TODO Auto-generated method stub
int left = leftChildIndex(i);
int right = rightChildIndex(i);
int min = i;
if(left<heapSize && list[left].compareTo(list[i])<0) {
min = left;
}
if(right<heapSize && list[right].compareTo(list[min])<0) {
min = right;
}
if(min != i) {
T temp = list[i];
list[i] = list[min];
list[min] = temp;
heapify(min);
}
}
public static void main(String[] args) {
Heap<Integer> heap = new MinHeap<>();
Integer[] nums = {4, 1, 3, 2, 16, 9, 10, 14, 8, 7};
heap.buildHeap(nums);
System.out.println(heap);
}
}
同样,我们用4, 1, 3, 2, 16, 9, 10, 14, 8, 7
作为测试用例,得到的结果为
1 2 3 4 7 9 10 14 8 16
其对应的堆结构为
堆排序
接下来,就可以调用堆数据结构实现堆排序了。
public class HeapSort<T extends Comparable<T>> {
/**
* 默认从小到大排序
* @param nums
*/
public void sort(T[] nums) {
Heap<T> heap = new MaxHeap<>();
heap.buildHeap(nums);
for(int i=nums.length-1; i>0; i--) {
T temp = nums[0];
nums[0] = nums[i];
nums[i] = temp;
heap.heapSize--;
heap.heapify(0); // 重新调整堆
}
}
/***
* 降序排序
* @param nums
*/
public void descendSort(T[] nums) {
Heap<T> heap = new MinHeap<>();
heap.buildHeap(nums);
for(int i=nums.length-1; i>0; i--) {
T temp = nums[0];
nums[0] = nums[i];
nums[i] = temp;
heap.heapSize--;
heap.heapify(0); // 重新调整堆
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Integer[] nums = {4, 1, 3, 2, 16, 9, 10, 14, 8, 7};
HeapSort<Integer> obj = new HeapSort<>();
// 升序排序
System.out.println("升序:");
obj.sort(nums);
for(int i=0; i<nums.length; i++) {
System.out.print(nums[i] + " ");
}
System.out.println();
// 降序排序
System.out.println("降序:");
obj.descendSort(nums);
for(int i=0; i<nums.length; i++) {
System.out.print(nums[i] + " ");
}
System.out.println();
}
}
用4, 1, 3, 2, 16, 9, 10, 14, 8, 7
作为测试用例的排序结果为
升序:
1 2 3 4 7 8 9 10 14 16
降序:
16 14 10 9 8 7 4 3 2 1