Go&Java算法之最小的K个数

300 阅读1分钟

这是我参与11月更文挑战的第17天,活动详情查看:2021最后一次更文挑战

最小的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

题解

方法一:堆——Java

我们用一个大根堆实时维护数组的前 k 小值。首先将前 k 个数插入大根堆中,随后从第 k+1 个数开始遍历,如果当前遍历到的数比大根堆的堆顶的数要小,就把堆顶的数弹出,再插入当前遍历到的数。最后将大根堆里的数存入数组返回即可。在下面的代码中,由于 C++ 语言中的堆(即优先队列)为大根堆,我们可以这么做。而 Python 语言中的堆为小根堆,因此我们要对数组中所有的数取其相反数,才能使用小根堆维护前 k 小值。

class Solution {
    public int[] getLeastNumbers(int[] arr, int k) {
        if (arr == null || arr.length == 0) {
            return new int[0];
        }
        PriorityQueue<Integer> queue = new PriorityQueue<>(new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o2 - o1;
            }
        });
        for (int i = 0; i < arr.length; i++) {
            queue.offer(arr[i]);
            if (queue.size() > k) {
                queue.poll();
            }
        }
        int[] res = new int[k];
        for (int i = 0; i < k; i++) {
            res[i] = queue.poll();
        }
        return res;
    }
}

方法二:排序——Java

本题使用排序算法解决最直观,对数组 arr 执行排序,再返回前 k 个元素即可。使用任意排序算法皆可

快速排序原理: 快速排序算法有两个核心点,分别为 “哨兵划分” 和 “递归” 。

哨兵划分操作: 以数组某个元素(一般选取首元素)为 基准数 ,将所有小于基准数的元素移动至其左边,大于基准数的元素移动至其右边。

如下图所示,为哨兵划分操作流程。通过一轮 哨兵划分 ,可将数组排序问题拆分为 两个较短数组的排序问题 (本文称之为左(右)子数组)。

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

方法二:排序——Go

var kk int
func getLeastNumbers(arr []int, k int) []int {
   kk=k
   quickSort(arr,0,len(arr)-1)
   return arr[:k]
}
func quickSort(nums[]int,left,right int)bool{
    if left>right{
        return false
    }
    key:=rand.Int()%(right-left+1)+left
    nums[left],nums[key]=nums[key],nums[left]
    i,j,pivot:=left,right,nums[left]
    for i<j{
        for i<j&&nums[j]>=pivot{ //如果是求前k大,这里nums[j]>=pivot改成 nums[j]<=pivot
            j--
        }
        for i<j&&nums[i]<=pivot{//如果是求前k大,这里nums[i]<=pivot改成 nums[i]>=pivot即可
            i++
        }
        nums[i],nums[j]=nums[j],nums[i]
    }
    nums[left],nums[i]=nums[i],nums[left]
     if kk<=left{//left之前的元素都已经有序了,那么当kk<=left时直接return即可
         return true
     }
     if quickSort(nums,left,i-1){
         return true
     }
     if quickSort(nums,i+1,right){
         return true
     }     
     return false
}