本文首发于我的博客html
本系列排序包括十大经典排序算法。git
Sort
里面实现了,交换,大小比较等方法。例如交换两个值,直接传入下标就能够了。其余的具体排序的类都继承抽象类Sort
。这样咱们就能专一于算法自己。/*
* 返回值等于0,表明 array[i1] == array[i2]
* 返回值小于0,表明 array[i1] < array[i2]
* 返回值大于0,表明 array[i1] > array[i2]
*/
protected int cmp(int i1, int i2) {
return array[i1].compareTo(array[i2]);
}
protected int cmp(T v1, T v2) {
return v1.compareTo(v2);
}
protected void swap(int i1, int i2) {
T tmp = array[i1];
array[i1] = array[i2];
array[i2] = tmp;
}
复制代码
由于本文不是专门讲解堆这种数据结构的,主要是讲解堆排序算法的。因此这里只给出堆的一些性质和结论github
2*i+1<=n-1 它的左子节点的索引为2*i+1
2*i+1>n-1 它没有左子节点
2*i+2<=n-1 它的右子节点的索引为2*i+2
2*i+2>n-1 它没有右子节点
public class HeapSort <T extends Comparable<T>> extends Sort<T>{
private int heapSize;
@Override
protected void sort() {
// 原地建堆 自下而上的下滤
heapSize = array.length;
// heapSize>>1-1 第一个非叶子节点的下标
for (int i = (heapSize>>1)-1; i >=0; i--) {
siftDown(i);
}
while (heapSize>1) {
// 交换堆顶元素和尾部元素
swap(0, heapSize-1);
//堆顶元素最大放到尾部了。下次就不须要考虑这个了。因此size要减1
heapSize--;
// 下滤 维护堆的性质
siftDown(0);
}
}
//让index位置的元素下滤
private void siftDown(int index) {
// 取出要下滤的坐标的值
T element = array[index];
int half = heapSize>>1;
// 第一个叶子节点的索引 == 非叶子节点的数量
// index<第一个叶子节点的索引
// 必须保证index位置是非叶子节点
while (index<half) {
// index的节点有2种状况
// 1.只有左子节点
// 2.同时有左右子节点
// 默认为左子节点跟它进行比较
int childIndex = (index<<1)+1;
T child = array[childIndex];
// 右子节点
int rightIndex = childIndex +1;
// 选出左右子节点最大的那个
if (rightIndex<heapSize && cmp(array[rightIndex], child)>0) {
childIndex = rightIndex;
child = array[childIndex];
}
if (cmp(element, child)>=0) {
break;
}
// 将子节点存放到index位置
array[index]= child;
// 从新设置index
index = childIndex;
}
// 将目标值存放到最终的index位置
array[index] = element;
}
}
复制代码
数据源:从1到20000之间随机生成10000个数据来测试算法
Integer[] array = Integers.random(10000, 1, 20000);bash
结果以下:数据结构
【HeapSort】 稳定性:false 耗时:0.008s(8ms) 比较次数:23.54万 交换次数:9999dom
【BubbleSort】 稳定性:true 耗时:0.502s(502ms) 比较次数:4999.50万 交换次数:2489.42万ide
【SelectionSort】 稳定性:true 耗时:0.115s(115ms) 比较次数:4999.50万 交换次数:9999性能
能够看到堆排序明显比选择排序和冒泡排序的性能高不少。测试