堆排序(Heapsort)
堆排序是指利用堆这种数据结构所设计的一种排序算法。
堆积是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。
算法过程
堆排序(HeapSort):移除位在第一个数据的根节点,并做最大堆调整的递归运算。
基于以上堆相关的操作,我们可以很容易的定义堆排序。例如,假设我们已经读入一系列数据并创建了一个最小堆,一个最直观的算法就是反复的调用del_min()函数,因为该函数总是能够返回堆中最大的值,然后把它从堆中删除,从而对这一系列返回值的输出就得到了该序列的降序排列。
堆排序的过程
- 创建一个堆H[0..n-1];
- 把堆首(最大值)和堆尾互换;
- 把堆的尺寸缩小1,并调用shift_down(0),目的是把新的数组顶端数据调整到相应位置;
- 重复步骤2,直到堆的尺寸为1;
代码
import com.lysongzi.Collection.BinaryHead;
/**
* Created by lysongzi on 16/3/2.
* 堆排序
*/
public class HeadSort<T> {
public static <T extends Comparable<? super T>> T[] sort(T []arr){
//创建一个堆,并完成建堆操作,时间复杂度O(n)
BinaryHead<T> head = new BinaryHead<>(arr);
//依次依次堆顶的元素
int i = 0;
//1.依次移除堆顶元素
//2.然后重建堆
//故时间复杂度为nlog(n)
while (!head.isEmpty() && i < arr.length){
arr[i] = head.deleteMin();
i++;
}
return arr;
}
}
二叉堆
上面的算法用到了一个辅助数据结构二叉堆,是基于最小堆来实现的。
package com.lysongzi.Collection;
/**
* Created by lysongzi on 16/3/2.
* 最小二叉堆
*/
import java.util.NoSuchElementException;
public class BinaryHead<T extends Comparable<? super T>> {
public BinaryHead(){
this(DEFAULT_SIZE);
}
@SuppressWarnings("unchecked")
public BinaryHead(int size){
currentSize = 0;
array = (T[]) new Comparable[size];
}
@SuppressWarnings("unchecked")
public BinaryHead(T[] dataArray){
/* 初始化数组 */
currentSize = dataArray.length;
array = (T[]) new Comparable[(currentSize+2)*11/10];
/* 拷贝传入的数组元素 */
int i = 1;
for(T x : dataArray)
array[i++] = x;
/* 构建堆 */
buildHead();
}
public void insert(T x){
/* 判断存储容器是否已满 */
if(currentSize == array.length -1)
enlargeArray(2 * array.length - 1);
/*
* 元素上潜到正确的位置
*/
int hole = ++currentSize;
for(;hole > 1 && x.compareTo(array[hole/2]) < 0; hole /= 2){
array[hole] = array[hole/2];
}
array[hole] = x;
}
public T findMin(){
return array[ROOT];
}
public T deleteMin(){
if(isEmpty())
throw new NoSuchElementException("BinaryHead is empty.");
/* 保存要移除的根元素值 */
T removeX = array[ROOT];
/* 将最后一个元素一道根节点,然后进行下潜操作移动到正确的位置 */
array[ROOT] = array[currentSize--];
percolateDown(ROOT);
return removeX;
}
public boolean isEmpty(){
return currentSize == 0;
}
@SuppressWarnings("unchecked")
public void clear(){
array = (T[]) new Comparable[array.length];
currentSize = 0;
}
public int size(){
return currentSize;
}
public boolean isBinaryHead(){
return isBinaryHead(ROOT);
}
private static int DEFAULT_SIZE = 128;
private static int ROOT = 1;
private T[] array;
private int currentSize;
private void percolateDown(int hole){
int child;
T tmp = array[hole];
for(; 2 * hole <= currentSize; hole = child){
child = hole * 2;
if(child != currentSize && array[child + 1].compareTo(array[child]) < 0)
child++;
if(array[child].compareTo(tmp) < 0)
array[hole] = array[child];
else
break;
}
array[hole] = tmp;
}
private void buildHead(){
for(int i = currentSize/2; i > 0 ; i--)
percolateDown(i);
}
@SuppressWarnings("unchecked")
private void enlargeArray(int size){
if(size <= array.length)
return;
T[] old = array;
array = (T[]) new Comparable[size];
for(int i = 1; i < old.length; i++)
array[i] = old[i];
}
private boolean isBinaryHead(int pos){
if(pos > currentSize)
return true;
int child = 2 * pos;
if(child <= currentSize && array[pos].compareTo(array[child]) > 0)
return false;
if((child + 1) <= currentSize && array[pos].compareTo(array[child+1]) > 0)
return false;
return isBinaryHead(child) && isBinaryHead(child + 1);
}
}