堆排序是简单选择排序的一种改进,改进的着眼点是:如何减少关键码的比较次数。简单选择排序在一趟中仅选出最小关键码,没有把一趟比较结果保存下来,因而记录的比较次数较多。堆排序在选出最小关键码的同时,也找出较小关键码,减少了在后面的选择中的比较次数,从而提高了整个排序的效率。
堆的定义: 堆是具有以下性质的完全二叉树:每个结点的值都小于或者等于其左右孩子结点的值(称为小根堆);或者每个结点的值都大于或者等于其左右孩子结点的值(称为大根堆)。
从堆的定义可以看出,一个完全二叉树如果是堆,则根结点一定是当前堆中所有结点的最大者或者最小者。以结点的编号作为下标,将堆用顺序存储结构存储,则堆对应于一组序列。
在调整堆的过程中,总是将根结点(即被调整结点)与左右孩子结点进行比较。若不满足堆的条件,则将根结点与左右孩子的较大值进行交换,这个调整过程一直进行到所有子树均为堆或将被调整结点交换到叶子结点为止。这个自堆顶至叶子结点的调整过程称为“筛选”。
堆排序是利用堆的特性进行排序的方法,其基本思想是:首先将待排序的记录构造成一个堆,此时,选出了堆中所有记录的最大即堆顶记录。然后将堆顶记录移走,并将剩余的记录调整成堆,这样又找出了次大的记录。以此类推,直到堆中只有一个记录为止。
package sort;
import java.util.Arrays;
public class HeapSortTest {
public static void main(String[] args) {
//数组中a[0],不参与排序,只对a[1]-a[len-1]排序
int a[] = {0 , 1, 3, 2, 16, 9, 10, 14, 8, 7};
int len=a.length-1;
heapSort(a,len);
System.out.println(Arrays.toString(a));
}
public static void heapSort(int r[],int n){
for(int i=n/2;i>=1;i--){
Sift(r,i,n);
}
System.out.println(Arrays.toString(r));//构建出的大根堆
for(int i=1;i<n;i++){
r[1]=r[n-i+1]+r[1];
r[n-i+1]=r[1]-r[n-i+1];
r[1]=r[1]-r[n-i+1];
Sift(r,1,n-i);
}
}
/**
* 筛选调整堆
* @param k 当前要筛选结点的编号为k,
* @param m 堆中最后一个结点的编号为m
*/
public static void Sift(int r[],int k,int m){
int i=k;//被筛选的结点
int j=2*i;//左子树
while(j<=m){
if(j<m&&r[j]<r[j+1])j++;
if(r[j]<r[i])break;
else{
r[i]=r[i]+r[j];
r[j]=r[i]-r[j];
r[i]=r[i]-r[j];
i=j; //被筛选结点位于原来的结点j的位置
j=2*i;
}
}
}
}