注:数据结构的堆,与jvm中的堆意义完全不同!栈也是- -
堆排序,顾名思义就是利用数据结构"堆"的特性,进行排序。堆的定义引用维基百科:
a heap is a specialized tree-based data structure that satisfies the heap property: If A is a parentnode of B, then the key (the value) of node A is ordered with respect to the key of node B with the same ordering applying across the heap. A heap can be classified further as either a "max heap" or a "min heap".
大意是:堆是“树”状数据结构的一种,满足每个根节点与它的子(叶)节点之间key(value)的值都是一致的,分为“大根堆”与“小根堆”。1991年的计算机先驱奖获得者、斯坦福大学计算机科学系教授罗伯特·弗洛伊德(Robert W.Floyd)和威廉姆斯(J.Williams)在1964年共同发明了著名的堆排序算法( Heap Sort ) (同时也是提出堆数据结构)
堆排序使用的树是完全二叉树,若设二叉树的高度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第 h 层从右向左连续缺若干结点(即先满左树)。
计算机中堆的实现,一般是用数组(Heaps are usually implemented in an array , wikipedia.org),如果使用链表那么做树的旋转效率太低。假如一个节点的下标为n,那么它的子节点下标为:
2n+1,2n+2 #在以0为起点的数组中
2n,2n+1 #在以1为起点的数组中
大根堆的分析:
1.在输入的数组a 上建立树状属性 root leftChild=a[2i+1] rightChild=a[2i+2]
2.进行排序,以满足大根堆的性质 root > leftChile root >rightChild
大根堆的实现:
1.堆排序的核心是递归,类似数学归纳法,
归纳内容(递归内容):对任意root节点为 a[i] 的子树进行root节点最大值的排序,则全树可有序。当本层节点进行值交换时候,会影响被交换的子树排序。过程:
if (leftChild>rightChild)
largest = leftChild
else
largest = rightChild
#找到最大值,与现在的根节点大小比较,如果大于root交换节点值
if (largest > root )
exchange(root , largest )
#由于交换了值,所以受影响的子树需要重新排序
heapSortOne( a[] , a[laegestTag])
2.实现递归入口部分:递归入口可以是 a[ length -1 ],但是这样效率很低。由root下标为0的数组a,a[n]的子节点为a[2n+1], 2[2n+2] 可得a[ length-1 ]的父节点为 a[ length-1 -1 ] /2
for ( fartherNodeTag = (a.length-2)/2 >= 0 ; fartherNodeTag--)
heapSortOne(a,fartherNodeTag );
#自底向上的树遍历
由以上很清晰地写出代码如下:
public class HeapSort {
//自底向上遍历方法
public void iterateHeap(int[] a){
for(int deep=(a.length-2)/2;deep>=0;deep--){
heapSortOne(a,deep);
}
}
//递归方法
public void heapSortOne(int[] a,int i){
int leftChild=i;
int rightChild=i;
int largestChildTag=i;
if((2*i+1)<a.length)
leftChild=a[2*i+1];
if((2*i+2)<a.length)
rightChild=a[2*i+2];
if(leftChild>rightChild)
largestChildTag=2*i+1;
else
largestChildTag=2*i+2;
if(compare(a, i, largestChildTag))
heapSortOne(a, largestChildTag);
}
//数据交换方法
private boolean compare(int[] a, int i, int t){
if(t<a.length && a[t]>a[i]){
if(a[t]>a[i]){
int temp=a[t];
a[t]=a[i];
a[i]=temp;
}
return true;
}
return false;
}
public static void main(String[] args) {
int[] a = {1,2,3,4,43,4,7,8,3,21,4,56,7,9,0,3534,7,8,9,213,346,67};
HeapSort hs =new HeapSort();
hs.iterateHeap(a);
for(int s : a){
System.out.print(s + " ");
}
}
}
K小问题:
有一个文件共有1E行,每行都是int值,且文件大于内存大小,请写出该文件中前K小个数。
K小问题使用大根堆,先建立长度为K的A[]大根堆,读入一个值Read判断Read<A[0],则继续调用heapSortOne(A , 0),即可维持大根堆。读完文件即可找出K小数。