剑指offer第二版——面试题40(java)

面试题:最小的k个数

题目:输入n个整数,找出其中最小的k个数,例如,输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4

【方法一】

将整数排序,排序后位于最前面的k个数则为最小的k个数。时间复杂度为O(nlogn)

 

【方法二——需要修改数组】

可以基于快排的部分算法来解决问题(Partition)。

可基于数组的第k个数字来调整,使得比第k个数字小的所有数字都位于数组的左边,比第k个数字大的所有数字都位于数组的右边。这样调整之后,位于数组左边的k个数字就是最小的k个数字

 

【方法三】

创建一个大小为k的数据容器,用于存储最小的k个数字。

每次从输入的n个整数中读入一个数,如果容器中已有数字少于k个,则将该数放入容器。

如果容器中已有k个数字,则找出已有k个数中的最大值,与待插入数字做比较,放入更小的数。

因此,在容器已满时,需要做的事包括:

1)找到最大数

2)删除最大数

3)插入新数

可用二叉树来实现该容器,这样可在O(logk)时间内实现上面步骤,因此对n个输入数字来说,总时间复杂度O(nlogk)

用二叉树来实现容器时,由于需要快速找到k个整数的最大数,因此使用最大堆来实现。(找到最大数是因为需要移除最大数)

在最大堆中,根节点的值总是大于它的子树中的任意节点值。

构造堆/调整堆: https://blog.csdn.net/xiao__gui/article/details/8687982

 

 

代码

【方法一】

排序方法在网上挺多的 直接排序之后输出前k个数字即可

【方法二】

public class Q40 {
	public static void main(String[] args) {
		int[] l = new int[] {4,5,1,6,2,7,3,8,0,-1};
		int[] mink = minK(l,5);
	}
	
	public static int[] minK(int[] l,int k) {
		int start = 0;
		int end = l.length-1;
		int loc = findLoc(l, start, end);
		while(loc!=k) {
			if(loc>k) {
				end = loc-1;
				loc = findLoc(l, start, end);
			}
			
			if(loc<k) {
				start = loc+1;
				loc = findLoc(l, start, end);
			}
		}
		
		int[] newl = new int[k]; 
		for(int i=0;i<k;i++) {
			newl[i]=l[i];
			System.out.printf("%d ",l[i]);
		}
		return newl;
	}
	
	
	public static int findLoc(int[] l, int start, int end) {
		int loc = start++;
		while(start<end) {
			while(l[start]<=l[loc] && start <end) {
				start++;
			}
			
			while(l[end]>=l[loc] && start < end) {
				end--;
			}

			int temp = l[end];
			l[end--] = l[start];
			l[start++] = temp;
		}
		int t = l[end];
		l[end] = l[loc];
		l[loc] = t;
		return end;	
	}
}

【方法三】

public class Q40_2 {
	public static void main(String[] args) {
		int[] l = new int[] {4,5,1,6,2,7,3,8};
		int[] s = minK(l,5);
		showList(s);
	}
	
	public static int[] minK(int[] l,int k) {
		int[] lk = new int[k];
		for(int i=0;i<k;i++) {
			lk[i] = l[i];
		}
		buildHeap(lk);
		int loc = k;
		while(loc<l.length) {
			if(l[loc]<lk[0]) {
				lk[0]=l[loc];
				heapify(0, lk);
			}
			loc++;
		}
		return lk;
	}
	
	// 建堆
	public static void buildHeap(int[] l) {
		for(int i=l.length/2-1;i>=0;i--) {
			heapify(i, l);
		}
	}
	
	// 调堆
	public static void heapify(int i,int[] l) {
		int left = i*2+1;
		int right = i*2+2;
		int biggest = i;
		if(left < l.length && l[left]>l[i]) {
			biggest = left;
		}
		if(right<l.length && l[biggest]<l[right]) {
			biggest = right;
		}
		
		if(i == biggest) {
			return;
		}
		swap(biggest, i, l);
		heapify(biggest, l);
	}
	
	// 交换数组中两个数
	public static void swap(int i,int j,int[] l) {
		int temp = l[i];
		l[i] = l[j];
		l[j] = temp;
	}
	
	// 打印数组
	public static void showList(int[] l) {
		for(int i=0;i<l.length;i++) {
			System.out.printf("%d ",l[i]);
		}
		System.out.println();
	}
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值