堆排序是一种选择排序
一,二叉堆
二叉堆是一棵完全二叉树,除底层外都填满,底层元素从左向右填入,二叉堆有两种:最大堆和最小堆。最大堆:父结点的键值总是大于或等于任何一个子节点的键值;最小堆:父结点的键值总是小于或等于任何一个子节点的键值。二叉堆简称为堆。
堆可以用数组存储。
private int[] heapArray;
private int maxSize;// 数组长度
private int heapSize;// 存放在数组中的堆元素个数
i结点的父结点为(i-1)/2 ,左子结点为2i+1,右子结点为2i+2
对于数组A[],长度为n,子数组A[n/2],......,A[n-1]都是树中的叶子。
在堆排序算法中我们使用最大堆,最小堆通常用于构造优先级队列。
二,保持最大堆的性质
如果根不是最大值,但是它的左子树和右子树都是最大堆,就需要把根元素下降到合适的位置,使整棵树调整成最大堆。
trickleDown()方法
比较根元素和最大的孩子间谁大,如果根元素小,就交换二者,层层下降,直到根元素大于最大的孩子为止。
public void trickleDown(int index) {
int largerChild;
int top=heapArray[index];
while(index<(heapSize>>1)){//该结点不是叶子,至少有一个孩子
int leftChild=2*index+1;
int rightChild=leftChild+1;
if(rightChild<heapSize&&heapArray[leftChild]<heapArray[rightChild])//如果右孩子存在并且比左孩子大
largerChild=rightChild;
else
largerChild=leftChild;
if(top>=heapArray[largerChild])//如果top比最大的孩子大,跳出循环
break;
heapArray[index]=heapArray[largerChild];//将最大的孩子提升上去
index=largerChild;//指针下降
}
heapArray[index]=top;//将根赋值给索引处
}
三,建立一个最大堆
叶子A[n/2],......,A[n-1]都可以看做正确的堆。
因此从A[n/2-1]开始,倒退到A[0],对这些不是叶子的结点依次用trickleDown()方法,将堆调整成最大堆。
public Heap(int[] arrays) {
maxSize = arrays.length;
heapSize = maxSize;
heapArray = arrays;
for(int i=(maxSize >> 1)-1;i>=0;i--){
trickleDown(i);
}
}
四,堆排序算法
排序结束后数组元素从小到大排列,而最大堆的根是最大元素。因此可以将堆顶根元素和堆的最后一个元素互换,来使最大元素到达最终位置,从堆中去除互换后的最后一个元素,将堆调整成最大堆。循环执行上述步骤,直到排序完毕。
上述步骤等价于先去除堆顶元素,调整最大堆,然后将被去除的堆顶元素赋值给堆后边的元素。
public HeapSort(int[] arrays) {
Heap mHeap=new Heap(arrays);//建立最大堆
for(int i=arrays.length-1;i>=0;i--){
int node=mHeap.remove();//去除堆顶元素,调整最大堆
mHeap.insertAt(i,node);//将被去除的堆顶元素赋值给堆后边的元素。
}
}
public void insertAt(int index, int node) {
// TODO 自动生成的方法存根
heapArray[index]=node;
}
public int remove() {
// TODO 自动生成的方法存根
int root=heapArray[0];
heapArray[0]=heapArray[--heapSize];//将堆的最后一个元素的值赋值给heapArray[0]
trickleDown(0);//调整堆
return root;
}
trickleDown方法的最差运行时间是O(logN),remove方法也是
堆排序算法的最差运行时间是O(NlogN),平均时间也是。堆排序算法比快速排序略慢,但是好处是对初始数据的分布不敏感,快速排序的最差运行时间是O(N^2)