堆的定义:
n个关键字序列K1,K2,…,Kn称为(Heap),当且仅当该序列满足如下性质(简称为堆性质):(1)ki<=k(2i)且ki<=k(2i+1)(1≤i≤ n/2)或(ki<=k(2i)且ki<=k(2i+1)(1≤i≤ n/2)),称为小根堆(或大根堆)。k(i)相当于二叉树的非叶子结点,K(2i)则是左子节点,k(2i+1)是右子节点。
若将此序列所存储的向量R[1..n]看做是一棵完全二叉树的存储结构,则堆实质上是满足如下性质的完全二叉树:树中任一非叶子结点的关键字均不大于(或不小于)其左右孩子(若存在)结点的关键字。
大根堆和小根堆:根结点(堆顶)的关键字是堆里所有结点关键字中最小者(或最大者)的堆称为小根堆(或大根堆)。注意:①堆中任一子树亦是堆。
堆排序:
堆排序利用了大根堆(或小根堆)堆顶记录的关键字最大(或最小)这一特征,使得在当前无序区中选取最大(或最小)关键字的记录变得简单。
(1)用大根堆排序的基本思想:
① 先将初始文件R[1..n]建成一个大根堆,此堆为初始的无序区
② 再将关键字最大的记录R[1](即堆顶)和无序区的最后一个记录R[n]交换,由此得到新的无序区R[1..n-1]和有序区R[n],且满足R[1..n-1].keys≤R[n].key
③由于交换后新的根R[1]可能违反堆性质,故应将当前无序区R[1..n-1]调整为堆。然后再次将R[1..n-1]中关键字最大的记录R[1]和该区间的最后一个记录R[n-1]交换,由此得到新的无序区R[1..n-2]和有序区R[n-1..n],且仍满足关系R[1..n-2].keys≤R[n-1..n].keys,同样要将R[1..n-2]调整为堆。
……
直到无序区只有一个元素为止。
java 代码实现建堆和堆排序如下:
public class HeapSort {
/*建队算法*/
public void Filterheap(int heap[],int root,int upper){
/*heap为待筛选的堆,root为堆的根结点,upper为无序区的上界*/
int child; //指向孩子结点
int item; //作为过渡变量
int r=root; //r指向根结点
child=2*root+1; //初始时为根结点的左孩子
while(child<upper){
/*比较左右孩子的关键字值,并取大者*/
if(child+1<upper && heap[child+1]>heap[child]){
child=child+1;
}
/*若根结点不满足堆的条件,则与孩子交换*/
if(heap[r]<heap[child]){
item=heap[r];
heap[r]=heap[child];
heap[child]=item;
r=child;
child=2*child+1;
}else break; //若根满足堆的条件,则不交换,并退出筛选
}
}
public void heapSort(int heap[],int n){
/*heap为待排序序列,n为序列长度*/
int i;
int item;//过渡变量
for(i=(n-1)/2;i>0;i--){ //从第一个非页结点开始创建初始堆
Filterheap(heap,i,n);
}
for(i=n-1;i>0;i--){ //做n-1趟排序,每次堆的大小减1
/*将堆顶元素与当前大根堆的最后一个元素交换*/
item=heap[0];
heap[0]=heap[i];
heap[i]=item;
/*筛选,将0~i的元素重新调整*/
Filterheap(heap,0,i);
/*显示排序过程*/
for(int j=0;j<n;j++){
System.out.print(heap[j]+", ");
}
System.out.println();
}
}
public static void main(String[] args) {
/*使用堆排序法进行排序*/
int heap[]=new int[]{9,8,7,6,5,4,3,2,1}; //把待排序的数存放在数组中
int n=heap.length;
HeapSort hs=new HeapSort();
hs.heapSort(heap,n);
}
}