堆排序 海量数据求前N大的值
最(大)小堆的性质:
(1)是一颗完全二叉树,遵循完全二叉树的所有性质。
(2)父节点的键值(大于)小于等于子节点的键值
堆的存储
一般都用数组来表示堆,i结点的父结点下标就为(i – 1) / 2。它的左右子结点下标分别为2 * i + 1和2 * i + 2。如第0个结点左右子结点下标分别为1和2。
海量数据前n大,并且n比较小,堆可以放入内存
【基本原理及要点】
最大堆求前n小,最小堆求前n大。方法,比如求前n小,我们比较当前元素与最大堆里的最大元素,如果它小于最大元素,则应该替换那个最大元 素。这样最后得到的n个元素就是最小的n个。适合大数据量,求前n小,n的大小比较小的情况,这样可以扫描一遍即可得到所有的前n元素,效率很高。
【扩展】
双堆,一个最大堆与一个最小堆结合,可以用来维护中位数。
建立堆
/**
* 将建立堆的过程看作是每次向堆中插入一个数据
* 每次插入数据都是在插入堆的尾部,然后进行堆调整
* 说明:每次调整的时候,只需要向上调整即可,由于每次调整之后的子节点的值总是大于父节点
* 当前插入放入节点向上一层一层调整时,始终保持子堆依然是最小堆结构
* 如 10
* / \
* 14 17
* / \ / \
* 18 15 20 37
* 插入节点13时,13为18的子节点,需交换,此时13为父节点,同时由于13小于14,再次交换
* 将建立堆的过程看作是每次向堆中插入一个数据
* 每次插入数据都是在插入堆的尾部,然后进行堆调整
* 说明:每次调整的时候,只需要向上调整即可,由于每次调整之后的子节点的值总是大于父节点
* 当前插入放入节点向上一层一层调整时,始终保持子堆依然是最小堆结构
* 如 10
* / \
* 14 17
* / \ / \
* 18 15 20 37
* 插入节点13时,13为18的子节点,需交换,此时13为父节点,同时由于13小于14,再次交换
* 注意这里不要调整13的子树的结构,因为13的子树都是向上调整子最小堆得来,因此13的子树本身满足最小堆结构
*/
*/
java代码实现
void minHeapBuild(int[] num,int index){ //num[0,1..index-1]为已经实现的最小堆,index为插入num[index]值,建立新的堆 int length=num.length; if(length<=0) return; int p=parent(index); while(p>=0&&index!=0){ if(num[index]>=num[p]) break; else{ //交换节点 int tmp=num[index]; num[index]=num[p]; num[p]=tmp; //重定位当前节点 index=p; p=parent(index); } } }
堆的删除
/** 堆的删除,每次都是删除堆顶的元素,删除后对堆进行调整* 具体做法是用堆尾部的元素代替堆顶元素,然后调整堆*/
java代码实现
void minHeapDelete(int[] num, int len){ int length=num.length; if(len>=length||len==0) return; int p=0; int child=leftChild(p); num[p]=num[len-1];//堆尾元素置于堆顶 while(child<len){ //找到左右子节点的较小值 if(child+1<len && num[child]>num[child+1]) child+=1; if(num[p]<=num[child]) break; else{ //交换节点 int tmp=num[child]; num[child]=num[p]; num[p]=tmp; //重定位当前父节点 p=child; child=leftChild(p); } } }
int parent(int i){return (i-1)/2;} int leftChild(int i){return 2*i+1;}
部分参考:http://blog.163.com/xychenbaihu@yeah/blog/static/132229655201351984231220/
http://blog.csdn.net/morewindows/article/details/6709644/