堆排序 海量数据求前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,再次交换
         *      注意这里不要调整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/

原文地址:https://www.cnblogs.com/wennian/p/5036912.html