求K个最小元素

算法:

1 排序后取前K个算法,比较笨

2排序算法中有些通过取本次循环最小的K实现排序的,因此排下前K个元素就可以实现取得最小的K个元素,比如selectionSort、bubbleSort、heapSort,对于selectionSort和bubbleSort,复杂度为O(Kn)。

对于堆排序来说,有一些自己的实现变体:

a)最简单的就是O(n)的复杂度实现最小堆,然后排序最小的K个元素,复杂度为O(n+Klogn)

b) 将前面的K个元素生成最大堆,复杂度为0(K),然后剩下的n-K个元素和最大堆堆首比较,比堆首小就互换,堆自身再更新一下重新生成堆,复杂度为(n-K)logK,加起来复杂度为O(nlogK)。最后得到的前K个元素就是所需的前K个元素

注:a)算法和b)算法的复杂度到底哪个高,可以相除n取无穷得到极限的方法,大于1就是分母高,小于就是分子高,等于就是一样(高数的知识)。

结果是a)比较好,但实际上在1000w数量级的时候。两者差15ms,因此实际相差不大。原pdf中所说空间复杂度不为1无法理解,数组用自己下标本身也可以构建堆,因此空间就是O(1)的复杂度。

c) a)的变体,实际上堆就是成成递推变大或者变小的数据结构,由于只要取K个元素,因此其实不必维护整个堆,取了第一个元素剩下还要取K-1个,因此只要保证K-1层是堆即可(下面的元素除了堆首沉底的还是都比上层大),也就是说调整min(logn,K-1)次(原pdf说要调整K次,应该是多调了一次)。依次类堆。当n相对于k较大的时候,复杂度为生成堆的O(n)+每次取值调整的O(K×K)。

这几个算法其实都很类似,启发点就是数组在某些情况下看成堆,可以给问题的解决带来便利

3 我的第一反应就是快速排序的变体,pdf推导了很多东西,随机选择pivot得到平均复杂度为0(n),头中尾选中值绝大多数情况下,复杂度就是0(n),还有中位数的中位数,有个神奇的名字:BFPRT算法。。看的晕乎乎的。。这个算法复杂度应该是最小的。

但实际上对于太多的数据量,由于不可能把所有数据都塞入内存,因此,选取要K个数构成最大堆塞入内存,然后用剩下的大量数据去用算法2 b)实现结果。

3的代码在各种排序算法(4)里有了,下面的是2c),破坏部分堆,有点意思。。。

代码如下(生成堆以后每次调整都是堆顶和下面的互换。。。。。。写过的都写了这么久。。。。):

//默认k<a.length&&k>0,不然没什么意义
	public void getMinKth(int[] a,int k){
		createHeap(a);
		for(int i=0;i<k;i++){
			int temp=a[0];a[0]=a[a.length-1-i];a[a.length-1-i]=temp;//每次堆顶互换
			int value1 = (int)Math.pow(2, k-i)-1-1; //沉k-i次的最大下标
			int value2 = a.length-1-(i+1);
			int end = value1 >  value2? value2 : value1 ; 
			adjustment(a,0,end);
		}
	}
	
	private void createHeap(int[] a){
		for(int i=(a.length-1-1)/2;i>=0;i--){
			adjustment(a,i,a.length-1);
		}
	}
	/**
	 * 
	 * @param a      需要调整位置的数组
	 * @param start  要调整元素的初始位置
	 * @param end	  要调整堆位置的最后端点
	 */
	private void adjustment(int[] a,int start,int end){
		while(2*start+1<=end){//存在左子树
			if(start*2+2<=end&&a[start*2+2]<=a[start*2+1]){//存在右子树并且右子树小于左子树
				if(a[start]>a[start*2+2]){//跟大于右子树
					int temp=a[start];a[start]=a[start*2+2];a[start*2+2]=temp;
					start = start*2+2;
				}else{
					break;
				}
			}else{//要替换的是左子树
				if(a[start]>a[start*2+1]){
					int temp=a[start];a[start]=a[start*2+1];a[start*2+1]=temp;
					start = start*2+1;
				}else{
					break;
				}
			}
		}
	}


原文链接:http://blog.csdn.net/v_july_v/article/details/6278484
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值