面试题 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
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。