自己实现了一个堆
import java.lang.*;
// 在堆的有关操作中,需要比较堆中元素的大小,所以Item需要extends Comparable
public class MaxHeap<Item extends Comparable> {
protected Item[] data;
protected int size;
protected int capacity;
// 构造函数, 构造一个空堆, 可容纳capacity个元素
public MaxHeap(int capacity){
data = (Item[])new Comparable[capacity];
size = 0;
this.capacity = capacity;
}
// 构造函数, 通过一个给定数组创建一个最大堆
// 该构造堆的过程, 时间复杂度为O(n)
public MaxHeap(Item arr[]){
int n = arr.length;
data = (Item[])new Comparable[n];
capacity = n;
for( int i = 0 ; i < n ; i ++ )
data[i] = arr[i];
size = n;
for(int i = (size - 1 - 1)/2; i >= 0 ; i --)
shiftDown(i);
}
// 返回堆中的元素个数
public int size(){
return size;
}
// 返回一个布尔值, 表示堆中是否为空
public boolean isEmpty(){
return size == 0;
}
// 像最大堆中插入一个新的元素 item
public void insert(Item item){
assert size < capacity;
data[size] = item;
shiftUp(size);
size ++;
}
// 从最大堆中取出堆顶元素, 即堆中所存储的最大数据
public Item extractMax(){
assert size > 0;
Item ret = data[0];
swap(0 , size - 1);
size--;
shiftDown(0);
return ret;
}
// 获取最大堆中的堆顶元素
public Item getMax(){
assert( size > 0 );
return data[0];
}
// 交换堆中索引为i和j的两个元素
private void swap(int i, int j){
Item t = data[i];
data[i] = data[j];
data[j] = t;
}
//********************
//* 最大堆核心辅助函数
//********************
private void shiftUp(int k){
while( k > 0 && data[(k-1)/2].compareTo(data[k]) < 0 ){
swap(k, (k-1)/2);
k = (k - 1) / 2;
}
}
private void shiftDown(int k){
while( (2 * k + 1) < size){
int j = 2 * k + 1; // 在此轮循环中,data[k]和data[j]交换位置
if( j + 1 < size && data[j + 1].compareTo(data[j]) > 0 )
j ++;
// data[j] 是 data[2*k]和data[2*k+1]中的最大值
if( data[k].compareTo(data[j]) >= 0 ) break;
swap(k, j);
k = j;
}
}
// 测试 MaxHeap
public static void main(String[] args) {
MaxHeap<Integer> maxHeap = new MaxHeap<Integer>(100);
int N = 100; // 堆中元素个数
int M = 100; // 堆中元素取值范围[0, M)
for( int i = 0 ; i < N ; i ++ )
maxHeap.insert( new Integer((int)(Math.random() * M)) );
Integer[] arr = new Integer[N];
// 将maxheap中的数据逐渐使用extractMax取出来
// 取出来的顺序应该是按照从大到小的顺序取出来的
for( int i = 0 ; i < N ; i ++ ){
arr[i] = maxHeap.extractMax();
System.out.print(arr[i] + " ");
}
System.out.println();
// 确保arr数组是从大到小排列的
for( int i = 1 ; i < N ; i ++ )
assert arr[i-1] >= arr[i];
}
}
堆排序1
- 依次放入堆,再依次取出;
import _04.selfimpl.MaxHeap;
public class HeapSort1 {
// 我们的算法类不允许产生任何实例
private HeapSort1(){}
// 对整个arr数组使用HeapSort1排序
// HeapSort1, 将所有的元素依次添加到堆中, 在将所有元素从堆中依次取出来, 即完成了排序
// 无论是创建堆的过程, 还是从堆中依次取出元素的过程, 时间复杂度均为O(nlogn)
// 整个堆排序的整体时间复杂度为O(nlogn)
public static void sort(Comparable[] arr){
int n = arr.length;
MaxHeap<Comparable> maxHeap = new MaxHeap<Comparable>(n);
for( int i = 0 ; i < n ; i ++ )
maxHeap.add(arr[i]);
for( int i = n - 1 ; i >= 0 ; i -- )
arr[i] = maxHeap.extractMax();
}
// 测试 HeapSort1
public static void main(String[] args) {
int N = 1000000;
Integer[] arr = SortTestHelper.generateRandomArray(N, 0, 100000);
SortTestHelper.testSort("_04.HeapSort1", arr);
}
}
输出:
HeapSort1 : 584ms
堆排序2
- 使用heapify操作直接将一个数组构建成堆,再从堆中依次取;
- 相比堆排序1,heapify创建堆的时间复杂度是O(n),更优,但实际测出来咋更慢呢...
import _04.selfimpl.MaxHeap;
public class HeapSort2 {
// 我们的算法类不允许产生任何实例
private HeapSort2(){}
// 对整个arr数组使用HeapSort2排序
// HeapSort2, 借助我们的heapify过程创建堆
// 此时, 创建堆的过程时间复杂度为O(n), 将所有元素依次从堆中取出来, 时间复杂度为O(nlogn)
// 堆排序的总体时间复杂度依然是O(nlogn), 但是比HeapSort1性能更优, 因为创建堆的性能更优
public static void sort(Comparable[] arr){
int n = arr.length;
MaxHeap<Comparable> maxHeap = new MaxHeap<Comparable>(arr);
for( int i = n - 1 ; i >= 0 ; i -- )
arr[i] = maxHeap.extractMax();
}
// 测试 HeapSort2
public static void main(String[] args) {
int N = 1000000;
Integer[] arr = SortTestHelper.generateRandomArray(N, 0, 100000);
SortTestHelper.testSort("_04.HeapSort2", arr);
return;
}
}
输出:
HeapSort2 : 1024ms