上一篇文章讲到了最大堆最小堆算法,今天又用最大堆写了个堆排序。正序使用最大堆,倒序使用最小堆。堆排序是简单选择排序算法的一种改进排序算法,时间复杂度为O(nlogn)。以正序为例,该算法的思想是利用数组构建最大堆,每次将最后一个结点与堆顶互换,这样最大的数就排到了数组的尾端。再用还未排好位置的其他结点重构最大堆,堆顶再与末结点互换。。。以此循环直到所有结点都排在正确的位置。
/**
* 堆排序
* @author dwl
*
*/
public class HeapSort {
private int[] data;
public HeapSort(int[] data){
this.data = data;
//从最后一个拥有子结点的结点开始重构最大堆
for(int i =data.length/2-1;i>=0;i--){
buildHeap(i, data.length);
}
}
/**
* 构建大顶堆
* @param i 父结点位置
* @param last 可交换子节点位置
*/
private void buildHeap(int i,int last){
int temp = 0;
int left =left(i);
int right = right(i);
//左右孩子都参与可重构
if(right<last){
//获取左右孩子中较大数的下标
temp = max(left, right);
if(data[temp]>data[i]){
//交换父子结点
swap(i, temp);
//交换结点后可能对原子结点的子树产生影响,重构该子结点的子数
buildHeap(temp, last);
}
}else if(left<last){//只有左孩子可以参加重构
//满足条件直接交换,无需重构
if(data[left]>data[i])
swap(i, left);
}
}
/**
* 根据父结点下标获取
* 左孩子下标
* @param i
* @return
*/
private int left(int i){
return ((i+1)<<1)-1;
}
/**
* 根据父结点下标获取
* 右孩子下标
* @param i
* @return
*/
private int right(int i){
return (i+1)<<1;
}
/**
* 交换两个结点
* @param i
* @param j
*/
private void swap(int i,int j){
int temp = data[i];
data[i] = data[j];
data[j] = temp;
}
/**
* 返回连个结点中较大的下标
* @param i
* @param j
* @return
*/
private int max(int i,int j){
if(data[i]>=data[j])
return i;
return j;
}
/**
* 最后一个结点与根结点交换,重构该结点前的其他结点
* @param last
*/
public void changeRootLast(int last){
int temp = data[0];
data[0] = data[last];
data[last] = temp;
//最后一次不再重构堆
if(last>1){
buildHeap(0, last-1);
}
}
/**
* 获取数组
* @return
*/
public int[] getData(){
return data;
}
/**
* 获取排序后的数组
* @return
*/
public int[] getSort(){
//循环将堆顶放入i的位置
for(int i = data.length-1;i>0;i--){
changeRootLast(i);
}
return data;
}
public static void main(String[] args) {
int[] data = {5,23,4,1,83,3,6,34,7};
HeapSort heap = new HeapSort(data);
int[] result =heap.getData();
//输出初始堆
for(int n:result){
System.out.print(n+";");
}
int[] sort = heap.getSort();
System.out.println();
//输出排好序的数组
for(int n:sort){
System.out.print(n+";");
}
}
}
测试结果如下图: