NC119 最小的K个数
最小的K个数
题目
输入整数数组 arr ,找出其中最小的 k 个数。例如,输入4、5、1、6、2、7、3、8这8个数字,则最小的4个数字是1、2、3、4。
示例 1:
输入:arr = [3,2,1], k = 2
输出:[1,2] 或者 [2,1]
示例 2:
输入:arr = [0,1,2,1], k = 1
输出:[0]
限制:
0 <= k <= arr.length <= 10000
0 <= arr[i] <= 10000
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/zui-xiao-de-kge-shu-lcof
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
题解-优先队列
Java实现了堆结构,为了熟悉知识点这里直接使用。
先把所有数据放进堆种,默认排序时升序。
输出前k个数就是最小的K个数。
class Solution {
public int[] getLeastNumbers(int[] arr, int k) {
int len = arr.length;
int [] res = new int[k] ;
if (k == 0 || len == 0) {
return res;
}
Queue<Integer> q = new PriorityQueue<>();
for(int num :arr){
q.offer(num); //全部放进去
}
for(int i=0;i<k;i++){
res[i] = q.poll(); //输出前k个数
}
return res;
}
}
这样数组有多大,优先队列就需要有多大。如一次队就要排一次序,能不能较少排序的次数?
这里想到了另外一种思路,保持优先队列的大小就为K,里面存储最小的K个元素。
当队列中没有k个元素时直接入队,当队列中满了K个元素时,新元素与队列中最大的元素比较,所以这里队列应该采用降序排列,这样队头元素就是队列中的最大值。
class Solution {
public int[] getLeastNumbers(int[] arr, int k) {
if (k == 0 || arr.length == 0) {
return new int[0];
}
//采用降序排列
Queue<Integer> pq = new PriorityQueue<>((v1, v2) -> v2 - v1);
for (int num: arr) {
//当队列中没有k个元素时直接入队
if (pq.size() < k) {
pq.offer(num);
} else if (num < pq.peek()) {//当队列中满了K个元素时,新元素与队列中最大的元素比较。比最大的小,最大的出队,小的入队
pq.poll();
pq.offer(num);
}
}
// 返回堆中的元素
int[] res = new int[pq.size()];
int idx = 0;
for(int num: pq) {
res[idx++] = num;
}
return res;
}
}
题解-快速排序
快排是选一个基准值,一趟之后基准值找到了自己的位置,左边部分是比基准值小的,右边部分是比基准值大的。
//主函数
public int[] getLeastNumbers(int[] arr, int k) {
int len = arr.length;
int [] res = new int[k] ;
if (k == 0 || len == 0) {
return res;
}
sort(arr,0,len-1,k);
for(int i=0;i<k;i++){
res[i] = arr[i];
}
return res;
}
//sort排序
void sort(int[]nums,int left,int right,int k){
if(left < right) { //如果基准点在最后一个元素,那么从下一轮递归中index+1>right了,用只有一个元素left==right判断就会出问题
//找基准点的最终位置,返回索引值。
int index = partiton(nums,left,right);
if(index+1==k){//说明排序已经完成了,left及left之前就是我们求得最小的k个数
return ;
}
if(index+1>k){//说明只需要对左半部分排序
sort(nums,left,index-1,k);
}else{//说明左边部分是需要的,不需要再排序了,直接排序右半边
sort(nums,index+1,right,k);
}
}
}
//需要找到基准值的位置
int partiton(int [] nums,int left,int right){
int tmp = nums[left]; //基准值
while(left<right){
while(left<right && right>= tmp)--right; //找到了比基准值小的
nums[left] = nums[right];
while(left<right && left <= tmp)++left; //找到了比基准值大的
nums[right] = nums[left];
}//找到了基准值的位置
nums[left] = tmp;
return left;
}