堆排序
堆排序是基于堆这种数据结构的一种排序算法,通过每一次弹出堆顶元素,实现排序。
预备知识:
- 堆是一棵完全二叉树,所以堆满足完全二叉树的性质。
- 大顶堆:每一个节点大于等于左右孩子节点的堆。
- 小顶堆:每一个节点小于等于左右孩子节点的堆。
- 左孩子下标 = 父节点下标 * 2 + 1;
- 右孩子下标 = 父节点下标 * 2 + 2;
- 父节点下标 = (左孩子或者右孩子节点下标 - 1) / 2
排序过程
首先,要有一个待排序的数组。
如果需要数组正序排列,那么就要构建大顶堆。
如果需要数组逆序排列,那么就要构建小顶堆。
之后循环对每一个数组元素进行插入。操作过程请参考我介绍堆的文章:https://www.jianshu.com/p/84296b80f5d3
通过利用预备知识里的公式进行比较和交换之后。
堆构建完成,如图所示:
再之后进行排序,其实排序的过程就是一个删除节点的过程,循环弹出所有的节点,就完成了排序,然而不同的是这里所说的删除不是真的删除了,而是弹出堆顶的最小元素,重新调整堆结构之后,将弹出的元素放到数组最后一个位置(弹出一个放到最后一个为,弹出第二个放在最后一会之前的位置)。
例如:弹出节点3。
用top记录3这个数值。
形成空穴,选出空穴左右孩子较小的与堆最后一个节点(16)比较,如果16小那么将16移入空穴,不然将左右孩子孩子较小的移入空穴。
之后重复上述步骤,如果空穴的左右孩子不全或者没有左右孩子则直接将最后一个节点插入空穴。
最后将top插入到末尾空穴中。
接下来的弹出操作中,就要忽略3这个节点,因为它已经弹出了,就不能计入节点总数中,所以currentSize在弹出后要 -1。
也就是说下一次进行弹出操作时,堆是这样的。
等到所有节点都弹出,也就实现了逆序排列。堆排序也就结束了。
全部代码如下
/**
* Created by ShouJingGuo on 2018/3/15.
*/
public class HeapSort<T extends Comparable<T>> {
private T[] heap;
private int size;
HeapSort(Object[] heap){
this.heap = (T[])heap;
this.size = heap.length;
init();
}
private void init(){
for(int i = 0; i < size; i++){
insert(i);
}
System.out.println(Arrays.toString(heap));
}
private void insert(int index){
T element = heap[index];
int hole = index;
for( ; (hole > 0) && (element.compareTo(heap[(hole-1)/2]) < 0); hole = (hole-1)/2){
heap[hole] = heap[(hole-1)/2];
}
heap[hole] = element;
}
//维护一个小顶堆
public void descSort(){
int currentSize = size - 1;
for(int i = 0; i < size; i++){
T top = heap[0];
int current = 0;
int child = 2 * current + 1;
while(currentSize > child){
if((child + 1 != currentSize) && (heap[child].compareTo(heap[child+1])>0)){
child++;
}
if(heap[child].compareTo(heap[currentSize]) < 0){
heap[current] = heap[child];
}else{
break;
}
current = child;
child = child * 2 + 1;
}
heap[current] = heap[currentSize];
heap[currentSize] = top;
currentSize--;
}
System.out.println("反序小顶堆排序:" + Arrays.toString(heap));
}
public static void main(String[] args) {
Integer[] heap = {5,3,36,51,8,5,4,10,20,48,52,12};
HeapSort heapSort = new HeapSort(heap);
heapSort.descSort();
}
}