最小的 k 个数

299 篇文章 1 订阅
227 篇文章 1 订阅

剑指 Offer 40. 最小的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

此题有很多解法,第一次写的时候只写了快速和堆,这次加上优先队列,并且重新分析(复盘没做出来),题目中要求寻找数组中最小的 k 个数字,那么我们可以一边排序一边寻找,运用快排和堆排的思想,可以做出(代码见下面两个),第一种使用最简单的把,优先队列

其实优先队列就可以看作一个堆,如果是数字小的优先,就是小顶堆,反之大顶堆,定义一个优先队列,使用 lambda 表达式重写排序规则(默认是小顶堆),然后只需要简单的向队列中无脑添加即可,最后取出前 k 个元素

class Solution {
    public int[] getLeastNumbers(int[] arr, int k) {
        PriorityQueue<Integer> queue = new PriorityQueue<>((x,y) -> x - y);
        int[] res = new int[k];
        for (int num : arr) {
            queue.offer(num);
        }
        for (int i = 0; i < k; i++) {
            res[i] = queue.poll();
        }
        return res;

    }
}

第一种方法如果在面试的时候,不建议使用,可以说思路,但是具体的堆排序过程最好还是自己实现

具体思路得先学会堆排序,代码见:https://blog.csdn.net/weixin_45962741/article/details/118543820?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522163531882716780262548301%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&request_id=163531882716780262548301&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2blogfirst_rank_v2~rank_v29-1-118543820.pc_v2_rank_blog_default&utm_term=%E5%A0%86%E6%8E%92%E5%BA%8F&spm=1018.2226.3001.4450

本题只是堆排序的一个小变形,当每次 swap 交换元素的时候,计数器加一,加到 k 的时候,说明已经排序出了 k 个最小的元素,此时直接返回数组即可

class Solution {
    public int[] getLeastNumbers(int[] arr, int k) {
        for (int i = arr.length / 2 - 1; i >= 0; i--) {
            adjustHeap(arr, i, arr.length);
        }
        int j = arr.length - 1;
        int[] res = new int[k];
        for (int i = 0; i < k; i++) {
            res[i] = arr[0];
            swap(arr, 0, j - i);
            adjustHeap(arr, 0, j - i);
        }
        return res;
    }
    public void swap(int[] arr, int left, int right) {
        int temp = arr[left];
        arr[left] = arr[right];
        arr[right] = temp;
    }
    public void adjustHeap(int[] arr, int i, int length) {
        int temp = arr[i];
        for (int k = 2 * i + 1; k < length; k = k * 2 + 1) {
            if (k + 1 < length && arr[k + 1] < arr[k]) {
                k++;
            }
            if (arr[k] < temp) {
                arr[i] = arr[k];
                i = k;
            } else {
                break;
            }
        }
        arr[i] = temp;
    }
}

堆排序的相关已经写完了,千万别忘了 21 世纪最牛的算法,快速排序,我们知道,快速排序一般以第一个数为基准,每次排序都能确定一个数的位置,该数前面的元素比它小,后面的元素比它大,那么此题就可以使用快速排序的变形,当某个数排好序的位置正好处于第 k 个位置时,说明前 k - 1 个元素都比这个元素小,此时也就找到了最小的 k 个数,见代码:

class Solution {
    public int[] getLeastNumbers(int[] arr, int k) {
        quickSort(arr, 0, arr.length - 1, k);
        return Arrays.copyOf(arr, k);
    }
    public void quickSort(int[] arr, int start, int end, int k) {
        if (start > end) {
            return;
        }
        int left = start, right = end;
        int temp = arr[start];
        while (left < right) {
            while (left < right && arr[right] >= temp) {
                right--;
            }
            arr[left] = arr[right];
            while (left < right && arr[left] <= temp) {
                left++;
            }
            arr[right] = arr[left];
        }
        arr[left] = temp;
        if (left == k) {
            return;
        } else if (left < k) {
            quickSort(arr, left + 1, end, k);
        } else {
            quickSort(arr, start, left - 1, k);
        }
    }
    
}

通过 leetCode 的执行结果,就可以发现,快排是最快的,yyds!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_努力努力再努力_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值