【LeetCode】剑指 Offer 40. 最小的k个数

文章目录

  • 【LeetCode】剑指 Offer 40. 最小的k个数
  • 一、笨比解法
  • 二、堆排序
  • 三、快速选择
  • 总结

一、笨比解法

选择排序变形

class Solution {public int[] getLeastNumbers(int[] arr, int k) {int[] res = new int [k];for(int i = 0; i < k; i++){int minIndex = i;for(int j = i; j < arr.length; j++){if(arr[minIndex] > arr[j]){int temp = arr[minIndex];arr[minIndex] = arr[j];arr[j] = temp;}}res[i] = arr[minIndex];}return res;}
}
  • 时间复杂度为 O(k * n):k 为需要找的最小数的个数,n 为数组长度
  • 空间复杂度为 O(k):使用了大小为 k 的数组

二、堆排序

比较直观的想法是使用堆结数据结构来辅助的到最小的 k 个数。堆的性质是每次可以找出最大或最小的元素。我们可以使用一个大小为 k 的最大堆(大顶堆),将数组中的元素依次入堆,当堆的大小超过 k 时,便将多出的元素从堆顶弹出。我们以数组 [5,4,1,3,6,2,9],k = 3 为例展示元素入堆的过程,如下面动图所示:

这样,由于每次从堆顶弹出的数据都是堆中最大的,最小的 k 个元素一定会留在堆里。这样,把数组中的元素全部入堆之后,堆中剩下的 k 个元素就是最大的 k 个数了

注意在动画中,我们并没有画出堆的内部结构,因为这部分内容并不重要。我们只需要直到堆每次会弹出最大的元素即可。在写代码的时候,我们使用的也是库函数中的优先队列数据结构,如 Java 中的 PriorityQueue,若想实现堆的结构,请参考【Java数据结构与算法】第十一章

class Solution{public int[] getLeastNumbers(int[] arr, int k){if(k == 0) return new int[0];if(arr.length <= k) return arr;Queue<Integer> heap = new PriorityQueue<>(k,(i1,i2) -> Integer.compare(i2,i1));for(int i : arr){if(heap.size() == 0 || heap.size() < k || i < heap.peek()){heap.offer(i);}if(heap.size() > k){heap.poll();}}int[] res = new int[k];int i = 0;for(int j : heap){res[i++] = j;}return res;}
}
  • 时间复杂度为 O(nlogk),每个元素都需要进行一次入堆操作,入堆出堆的时间复杂度为 O(logk)
  • 空间复杂度为 O(k),使用了一个大小为 k 的堆

三、快速选择

题目只要求返回最小的 k 个数,对这 k 个数的顺序并没有要求。因此,只需要将数组划分为最小的 k 个数和其他数两部分即可,而快速排序的哨兵划分可完成此目标

根据快速排序原理,如果某次哨兵划分后基准数正好是第 k + 1 小的数字,那么此时基准数左边的所有数字便是题目所求的最小的 k 个数

根据此思路,考虑在每次哨兵划分后,判断基准数在数组中的索引是否等于 k,若 true 则直接返回此时数组的前 k 个数字即可

getLeastNumbers() 函数:

  1. 若 k 大于数组长度,则直接返回整个数组
  2. 执行并返回 quick_sort() 即可,quick_sort() 的功能不是排序整个数组,而是搜索并返回最小的 k 个数

哨兵划分:

  • 划分完毕后,基准数为 arr[i] ,左 / 右子数组区间分别为 [l,i-1],[i+1,r]

递归或返回:

  • 若 k < i,代表 k + 1 小的数字在左子数组中,则递归左子数组
  • 若 k > i,代表 k + 1 小的数字在右子数组中,则递归右子数组
  • 若 k = i,代表此时 arr[k] 即为第 k + 1 小的数字,则直接返回数组前 k 个数字即可
class Solution {public int[] getLeastNumbers(int[] arr, int k) {if(k >= arr.length) return arr;return quickSort(arr,0,arr.length-1,k);}private static int[] quickSort(int[] arr, int l, int r, int k) {int i = l;int j = r;while(i < j){while(i < j && arr[j] >= arr[l]) j--;while(i < j && arr[i] <= arr[l]) i++;swap(arr,i,j);}swap(arr,i,l);if(i > k) quickSort(arr,l,i-1,k);if(i < k) quickSort(arr,i+1,r,k);return Arrays.copyOf(arr,k);}private static void swap(int[] arr,int i,int j){int temp = arr[i];arr[i] = arr[j];arr[j] = temp;}
}
  • 时间复杂度 O(n),其中 n 为数组元素数量,对于长度为 N 的数组执行哨兵划分操作的时间复杂度为 O(n)。每轮哨兵划分后根据 k 和 i 的大小关系选择递归,由于 i 分布的随机性,则向下递归子数组的平均长度为 n/2。因此平均情况下哨兵划分操作一共有 2n - 1(等比数列求和),即总体时间复杂度为 O(n)
  • 空间复杂度 O(logn):划分函数的平均递归深度为 O(logN)

总结

由于对于快排还没有到炉火纯青的地步,以及对堆排序比较陌生,我想出来的方法是使用选择排序,因为选择排序不需要将整个数组排序才能退出,选择排序即使中途退出,也能保证前 k 个数是最小且有序的数,这与题目只需要前 k 个数有序契合

【LeetCode】剑指 Offer 40. 最小的k个数相关推荐

  1. Leetcode 剑指 Offer 40. 最小的k个数 (每日一题 20210825)

    输入整数数组 arr ,找出其中最小的 k 个数.例如,输入4.5.1.6.2.7.3.8这8个数字,则最小的4个数字是1.2.3.4.示例 1:输入:arr = [3,2,1], k = 2 输出: ...

  2. 【Java】 剑指offer(40) 最小的k个数

    本文参考自<剑指offer>一书,代码采用Java语言. 更多:<剑指Offer>Java实现合集   题目 输入n个整数,找出其中最小的k个数.例如输入4.5.1.6.2.7 ...

  3. 最小的k个数 java_【Java】 剑指offer(40) 最小的k个数

    本文参考自<剑指offer>一书,代码采用Java语言. 题目 输入n个整数,找出其中最小的k个数.例如输入4.5.1.6.2.7.3.8这8个数字,则最小的4个数字是1.2.3.4. 思 ...

  4. 剑指 Offer 40. 最小的k个数

    题目 输入整数数组 arr ,找出其中最小的 k 个数.例如,输入4.5.1.6.2.7.3.8这8个数字,则最小的4个数字是1.2.3.4. 示例 输入:arr = [3,2,1], k = 2 输 ...

  5. LeetCode_Heap_剑指 Offer 40. 最小的k个数 【堆,泛型实现,自定义比较器】【C++/java】【简单】

    目录 一,题目描述 英文描述 中文描述 示例与说明 二,解题思路 1,手动实现堆--C++泛型实现 2,手动实现堆--java泛型实现 3,快速使用堆--C++ 优先队列 pop_heap().pus ...

  6. 剑指offer 40.最小的 K 个数 python代码

    题目 寻找数组中的最小的k个数,也叫topk问题. 牛客网测试地址 注意: 牛客网的提交需要将最终的结果排序 思路 快速排序的 partition() 方法,会返回一个整数 j 使得 a[l-j-1] ...

  7. 【力扣刷题】剑指 Offer 40. 最小的k个数(大顶堆)

    题目: 输入整数数组 arr ,找出其中最小的 k 个数.例如,输入4.5.1.6.2.7.3.8这8个数字,则最小的4个数字是1.2.3.4. 示例: 输入:arr = [3,2,1], k = 2 ...

  8. 【算法】剑指 Offer 40. 最小的k个数 【重刷】

    1.概述 输入整数数组 arr ,找出其中最小的 k 个数.例如,输入4.5.1.6.2.7.3.8这8个数字,则最小的4个数字是1.2.3.4. 示例 1: 输入:arr = [3,2,1], k ...

  9. 174. 地下城游戏;剑指 Offer 40. 最小的k个数;378. 有序矩阵中第K小的元素;703. 数据流中的第K大元素

    一些恶魔抓住了公主(P)并将她关在了地下城的右下角.地下城是由 M x N 个房间组成的二维网格.我们英勇的骑士(K)最初被安置在左上角的房间里,他必须穿过地下城并通过对抗恶魔来拯救公主. 骑士的初始 ...

最新文章

  1. 【转】HTML5 本地存储五种方案
  2. ps意外崩溃_充满意外的数学中考
  3. 中石油训练赛 - 斗地主(bfs)
  4. python爬虫实际应用_如何使用python爬虫论坛?
  5. LeetCode 788. Rotated Digits
  6. 单例模式-双重检查加锁
  7. ios多线程之NSThread头文件详解
  8. count(1)与count(id)与count(*)效率,以及覆盖索引,索引下推
  9. android view 画文字,【Android自定义View】绘图之文字篇(三)
  10. JAVA程序员必看的15本书-JAVA自学书籍推荐
  11. 电话机器人源码安装教程
  12. 我珍藏很久的网盘资源搜索网站和下载神器
  13. pdfobject.js和pdf.js的详解
  14. 向淘宝学运营:移动MM速长成
  15. [Azure - VM] 解决办法:无法通过SSH连接VM,解决错误:This service allows sftp connections only.
  16. 完美解码播放器(PotPlayer)关闭自动播放下一首的方法(找了好久)
  17. uva 10977 Enchanted Forest 魔幻森林
  18. Linux ln 命令是什么?C/C++代码实现
  19. 求多边形凸包(线性算法)--陈氏凸包算法--
  20. 【高阶数据结构】手撕红黑树(超详细版本)

热门文章

  1. checkbox 选中_纯CSS修改checkbox复选框样式
  2. 如何查看服务器数据库修改密码,如何查看服务器数据库密码
  3. python中if语句最多支持的分支是_Python多分支if语句的使用
  4. linux下安装nginx tar包,Linux环境下Nginx的安装
  5. Linux从入门到精通——文件传输
  6. LeetCode OJ Symmetric Tree 判断是否为对称树(AC代码)
  7. [译] 如何用ps制作泼水字
  8. ASP.NET中的事件处理
  9. git commit撤销_Git commit 提交一堆记录?试试撤回 commit 操作,你会爱上它
  10. 方便好用的在线CPU性能调优工具--perf的常见用法