LeetCode-面试题17-14-最小K个数

面试题 17.14. 最小K个数

  • 题目

    设计一个算法,找出数组中最小的k个数。以任意顺序返回这k个数均可。

示例 1:

输入: arr = [1,3,5,7,2,4,6,8], k = 4
输出: [1,2,3,4]

  • 难度:中等

  • 分类:多看

  • 题解

    首先看到题,我的第一思路就是暴力破解,先数组排列,然后取前k个值返回。代码如下:

    class Solution {
        public int[] smallestK(int[] arr, int k) {
        	Arrays.sort(arr);
        	int []res=new int[k];
        	for(int i=0;i<k;i++) {
        		res[i]=arr[i];
        	}
        	return res;
        }
    }
    

    那么按照以上方式提交的结果则是:

在这里插入图片描述

  • 优化

    那么通过暴力破解的方法,我们可以看见是能够通过测试的,但是这种中等题不可能就让我们用暴力破解的方法就结束了。这里我们可以想想之前做过的每日一题295. 数据流的中位数,这里我们用到了大小堆进行排序,那么这题我们也可以用到这个大小堆,对于大堆来说,父节点比子节点值更大。小堆则是子节点比父节点更大。在这题中,我们可以通过先入堆k个数,那么他会将最大的数排序到根节点上,我们再比较根节点与接下来数组的值的大小,若大,则将根节点弹出再入堆数组值,若小则继续下一个。一直将数组遍历一遍。

    代码如下:

        public int[] smallestK(int[] arr, int k) {
        	//大堆,父节点的值比子节点值大
        	PriorityQueue<Integer> min=new PriorityQueue<>((a,b)->(b-a));
        	int []res=new int[k];
        	
        	//不采用这个方法讲所有的值全部放入堆栈,因为序列越长,堆栈排列时间越长,并且堆栈的排序并不是严格的排序,也就是说它排序的标准是:父亲节点的值比子节点大,但是父亲的两个左右子节点,并不是左边的一定比右子节点小。
    //    	for(int i=0;i<arr.length;i++) {
    //    		min.add(arr[i]);
    //    	}
    //    	
        	if(k==0) {
        		return res;
        	}
        	//先放入k个数进行排列
        	for(int i=0;i<k;i++) {
        		min.offer(arr[i]);
        	}
        	
        	for(int i=k;i<arr.length;i++) {
        		if(arr[i]<min.peek()) {
        			min.poll();
            		min.offer(arr[i]);	
        		}
        	}
        	
        	for(int i=0;i<k;i++) {
        		res[i]=min.poll();
        	}
        	return res;
        }
    

    那么取得的结果为:

在这里插入图片描述

  • 再优化

通过上面的结果其实我们能够发现,在执行时间上还是太低,那么我们知道这道题的解题思路是排序,那么我们可以考虑对数组进行快速排序,来提升排序的时间复杂度。

一趟快速排序的算法是:

1)设置两个变量i、j,排序开始的时候:i=0,j=N-1;

2)以第一个数组元素作为关键数据,赋值给key,即key=A[0];

3)从j开始向前搜索,即由后开始向前搜索(j–),找到第一个小于key的值A[j],将A[j]和A[i]的值交换;

4)从i开始向后搜索,即由前开始向后搜索(i++),找到第一个大于key的A[i],将A[i]和A[j]的值交换;

5)重复第3、4步。
当然通过上面的讲诉还是过于抽象,,下面我们举个例子来说明:

假设一开始序列{xi}是:5,3,7,6,4,1,0,2,9,10,8。

此时,ref=5,i=1,j=10,从后往前找,第一个比5小的数是x8=2,因此序列为:2,3,7,6,4,1,0,5,9,10,8。

此时i=1,j=8,从前往后找,第一个比5大的数是x3=7,因此序列为:2,3,5,6,4,1,0,7,9,10,8。

此时,i=3,j=8,从第8位往前找,第一个比5小的数是x7=0,因此:2,3,0,6,4,1,5,7,9,10,8。

此时,i=3,j=7,从第3位往后找,第一个比5大的数是x4=6,因此:2,3,0,5,4,1,6,7,9,10,8。

此时,i=4,j=7,从第7位往前找,第一个比5小的数是x6=1,因此:2,3,0,1,4,5,6,7,9,10,8。

此时,i=4,j=6,从第4位往后找,直到第6位才有比5大的数,这时,i=j=6,ref成为一条分界线,它之前的数都比它小,之后的数都比它大,对于前后两部分数,可以采用同样的方法来排序。

class Solution {
    public int[] smallestK(int[] arr, int k) {
    	quickSort(arr,0, arr.length-1 );
    	int []res=new int[k];
    	for(int i=0;i<k;i++) {
    		res[i]=arr[i];
    	}
    	return res;
    }
    

    public void quickSort(int[] nums, int low, int height) {
        int i = low;
        int j = height;
        
        if (i > j) {
            return;
        }
        int k = nums[i];
 
        while (i < j) {
        	//从后往前找比k小的数
            while (i < j && nums[j] > k) { 
                j--;
            }
            //从前往后找比k大的数
            while (i < j && nums[i] <= k) { //找出大的数
                i++;
            }
            //进行交换
            if (i < j) {//交换
                int swap = nums[i];
                nums[i] = nums[j];
                nums[j] = swap;
            }
 
        }
       //将k交换到最终位置
        k = nums[i];
        nums[i] = nums[low];
        nums[low] = k;
 
        //对左边进行排序,递归算法
        quickSort(nums, low, i - 1);
        //对右边进行排序
        quickSort(nums, i + 1, height);
    }
}

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/smallest-k-lcci
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值