算法分析:
堆排序和归并排序一样,实践时间复杂度是O(nlgn),不同于归并排序的是,堆排序是一种原址排序。本文介绍最大堆。
代码中关键操作:
maxHepify:时间复杂度是O(lgn),是维护堆性质的关键。
buildMaxHeap:建立最大堆,时间复杂度是O(n);
heapSort:通过调用exactMax,按顺序得到一个排序的数组,时间复杂度是O(nlgn);
insert:插入新元素,通过调用increseKey私有方法操作,时间复杂度是O(lgn);
当然,还有max(),exactMax()方法。更加具体分析请参见《算法导论》,下面是我的实现代码和测试代码:
import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.Comparator;
// 逻辑上堆是从index = 1开始,实际上从index = 0开始,
// 故各方法中常见index - 1;
public class MaxHeap<T extends Comparable<T>> {
Object[] array;
private final int DEFAULT_CAPACITY = 100;
private final float LOAD = 0.75F;
private int size = 0;
private Comparator<T> comparator;
// 构建一个空堆
public MaxHeap() {
array = new Object[DEFAULT_CAPACITY];
}
// 构建一个堆,包含array中的数据,array初始容量是array长度两倍
public MaxHeap(T[] array) {
this(array, null);
}
// 构建一个堆,包含array中的数据,array初始容量是array长度两倍,并按提供的比较器大小比较
public MaxHeap(T[] array, Comparator<T> comparator) {
this.array = Arrays.copyOfRange(array, 0, array.length * 2);
size = array.length;
this.comparator = comparator;
buildMaxHeap();
}
// 代理堆中对象的比较方法
@SuppressWarnings("unchecked")
private int compare(Object o1, Object o2) {
if (comparator == null)
return ((T) o1).compareTo((T) o2);
else
return comparator.compare((T) o1, (T) o2);
}
// 使得初始化堆满足最大堆性质
private void buildMaxHeap() {
for (int i = size / 2; i >= 1; i--)
maxHeapify(i);
}
// 假设i+1, i+2...已经满足最大堆性质,执行此方法后,
// 使得i, i+1,i+2.....满足最大堆性质
private void maxHeapify(int i) {
int l = left(i);
int r = right(i);
int largest;
if (l <= size && compare(array[l - 1], array[i - 1]) > 0)
largest = l;
else
largest = i;
if (r <= size && compare(array[r - 1], array[largest - 1]) > 0)
largest = r;
if (largest != i) {
swap(i, largest);
maxHeapify(largest);
}
}
// 交换i1,i2处的值
private void swap(int i1, int i2) {
Object temp = array[i1 - 1];
array[i1 - 1] = array[i2 - 1];
array[i2 - 1] = temp;
}
// 返回左孩子位置
private int left(int parent) {
return 2 * parent;
}
// 返回右孩子位置
private int right(int parent) {
return 2 * parent + 1;
}
// 返回父节点位置
private int parent(int child) {
return child / 2;
}
// 返回堆中最大值
@SuppressWarnings("unchecked")
public T max() {
return (T)array[0];
}
// 返回并删除堆中最大值
@SuppressWarnings("unchecked")
public T exactMax() {
if(size < 1)
throw new IndexOutOfBoundsException("堆中没有元素");
T max = (T)array[0];
array[0] = array[size - 1];
size = size - 1;
maxHeapify(1);
return max;
}
// 插入一个值,如果堆已达到负载,将堆容量扩大一倍
public void insert(T obj) {
array[size] = obj;
size = size + 1;
if((float)size / array.length >= LOAD) {
array = Arrays.copyOfRange(array, 0, array.length * 2);
}
increseKey(size, obj);
}
// 按顺序返回堆中元素的数组,将堆变成空堆
public T[] heapSort() {
if(size == 0)
return null;
else {
@SuppressWarnings("unchecked")
T[] result = (T[])Array.newInstance(array[0].getClass(), size);
int len = size;
for(int i = 0; i < len; i++)
result[i] = exactMax();
return result;
}
}
// 判断堆是否是空堆
public boolean empty() {
return size == 0;
}
// 增加堆中指定index处的数据
private void increseKey(int i, T key) {
if(compare(array[i - 1], key) > 0)
throw new UnsupportedOperationException("新值不能比原值小");
array[i - 1] = key;
while(i > 1 && compare(array[parent(i) - 1], array[i - 1]) < 0) {
swap(parent(i), i);
i = parent(i);
}
}
}
测试代码(其中涉及到的SortComparble和Point类,请参见我的上一篇博客:):
import java.util.Arrays;
import NearestPoints.Point;
import NearestPoints.PointGenerator;
import NearestPoints.PointYComparaotr;
import NearestPoints.SortComparable;
// SortComparble 已被确认正确
public class TestMaxHeap {
private static final int SIZE = 100;
public static void main(String[] args) {
// 生成点数组
Point[] points = new Point[SIZE];
for(int i = 0; i < points.length; i++)
points[i] = new PointGenerator(1000).next();
// 建立默认堆,并得到堆排序的数组heap1Sort
MaxHeap<Point> heap1 = new MaxHeap<>();
for(int i = 0; i < points.length; i++)
heap1.insert(points[i]);
Point[] heap1Sort = heap1.heapSort();
// 调用SortComparable,将points从小到大按默认比较方法排序
SortComparable.sort(points);
// 逐个比较SortComparble排序和MaxHeap排序结果,有不一致输出 “error”
for(int i = 0; i < points.length; i++)
if(!points[i].equals(heap1Sort[heap1Sort.length - i - 1]))
System.out.println("error" + i + points[i] + heap1Sort[heap1Sort.length - i - 1]);
// 建立有比较器的堆,并得到堆排序的数组heap2Sort
MaxHeap<Point> heap2 = new MaxHeap<>(points, new PointYComparaotr());
Point[] heap2Sort = heap2.heapSort();
// 逐个比较SortComparble排序和MaxHeap排序结果,有不一致输出 “error”
SortComparable.sort(points, new PointYComparaotr());
for(int i = 0; i < points.length; i++)
if(!points[i].equals(heap2Sort[heap2Sort.length - i - 1]))
System.out.println("error" + i + points[i] + heap2Sort[heap2Sort.length - i - 1]);
}
}