输入整数数组 arr ,找出其中最小的 k 个数。例如,输入4、5、1、6、2、7、3、8这8个数字,则最小的4个数字是1、2、3、4。
输入:arr = [3,2,1], k = 2
输出:[1,2] 或者 [2,1]
输入:arr = [0,1,2,1], k = 1
输出:[0]
0 <= k <= arr.length <= 10000
0 <= arr[i] <= 10000
调用Arrays.sort()方法排序,Arrays.copyOf()方法截取数组
class Solution {
public int[] getLeastNumbers(int[] arr, int k) {
//排序取前k项
Arrays.sort(arr);
return Arrays.copyOf(arr, k);
}
}
快速排序求解。这里我们只需要知道前k项的元素是什么,并不关注该数组是否完成了排序,故每进行一次快排后都将中头尾两指针相遇的下标返回与k-1进行比较。当两值相等时使用Arrays.copyOf()方法截取数组。
自己写快排的时候遇到了几个坑,导致运行的时候要么就是没正确排序,要么就是程序一直卡住不动,这里总结了一下写快速排序需要注意的点:
详细的解释见下方代码,解法参考:LeetCode题解地址
class solution{
public int[] getLeastNumbers(int[] arr, int k) {
if (k == 0 || arr.length == 0) {
return new int[0];
}
// 最后一个参数表示我们要找的是下标为k-1的数
return quickSearch(arr, 0, arr.length - 1, k - 1);
}
private int[] quickSearch(int[] nums, int low, int high, int k) {
// 每快排切分1次,找到排序后下标为j的元素,如果j恰好等于k就返回j以及j左边所有的数;
int i = partition(nums, low, high);
if (i == k) {
return Arrays.copyOf(nums, i + 1);
}
// 否则根据下标j与k的大小关系来决定继续切分左段还是右段。
else if(i<k){
return quickSearch(nums, i+1, high, k);
}else{
return quickSearch(nums, low, i-1, k);
}
}
// 快排切分,返回下标j,使得比nums[i]小的数都在i的左边,比nums[i]大的数都在i的右边。
private int partition(int[] nums, int low, int high) {
//可防止程序卡住不运行
if(low>=high){
return high;
}
//基准值,为了方便一般选用头尾位置的值,这里选用的是low的值
int pivot=nums[low];
//数组头尾双指针
int i=low;
int j=high;
//i所指处的指比基准值大就停下,j所指处的指比基准值小就停下,然后两个位置的值交换
while(i<j){
while(i<j&&nums[j]>pivot){
j--;
}
//由于遍历是第一个就是基准值,所以这里使用<=
while(i<j&&nums[i]<=pivot){
i++;
}
if(i<j){
int temp = nums[j];
nums[j] = nums[i];
nums[i] = temp;
}
}
//当i,j相遇时交换相遇位置
//交换完位置之后左边都是小于基准值的元素,右边都是大于基准值的元素
nums[low]=nums[i];
nums[i]=pivot;
//返回这个相遇位置的下标
return i;
}
}