当前位置: 首页 > news >正文

剑指offer-29、最⼩的k个数

题⽬描述

输⼊ n 个整数,找出其中最⼩的 K 个数。例如输⼊ 4,5,1,6,2,7,3,8 这 8 个数字,则最⼩的 4 个数字是 1,2,3,4 。

思路及解答

排序法

最直接的思路是将数组排序后取前k个元素

public ArrayList<Integer> GetLeastNumbers_Solution(int[] input, int k) {ArrayList<Integer> result = new ArrayList<>();if (input == null || k <= 0 || k > input.length) {return result; // 处理非法输入}Arrays.sort(input); // 使用Java内置排序,时间复杂度O(n log n)for (int i = 0; i < k; i++) {result.add(input[i]); // 取前k个元素}return result;
}
  • 时间复杂度​:主要由 Arrays.sort() 决定,为 O(n log n)
  • 空间复杂度​:O(k),用于存储结果的列表

大顶堆法(推荐)

维护一个大小为k的大顶堆​(Max-Heap),堆顶是当前k个数中的最大值。遍历数组,如果当前数比堆顶小,则替换堆顶并调整堆

这⾥不展开最⼤堆和最⼩堆的具体讲解,什么是最⼤堆/最⼩堆呢?

  • ⼤/⼩堆树是完全⼆叉树
  • ⼤/⼩堆树的每⼀个节点不⼩于/不⼤于其孩⼦节点。
public ArrayList<Integer> GetLeastNumbers_Solution(int[] input, int k) {ArrayList<Integer> result = new ArrayList<>();if (input == null || k <= 0 || k > input.length) {return result;}// 创建一个大顶堆,使用自定义比较器实现降序排列PriorityQueue<Integer> maxHeap = new PriorityQueue<>((a, b) -> b - a);for (int num : input) {if (maxHeap.size() < k) {maxHeap.offer(num); // 堆未满,直接加入} else if (num < maxHeap.peek()) { // 当前数比堆顶最大值小maxHeap.poll(); // 移除堆顶最大值maxHeap.offer(num); // 将当前数加入堆}}result.addAll(maxHeap);return result;
}
  • 时间复杂度​:O(n log k)。每次插入或删除堆的操作需要 O(log k) 时间,共需进行 n 次这样的操作
  • 空间复杂度​:O(k),堆的大小

堆这种数据结构适合处理海量数据​(数据无法一次性装入内存),或者当 ​k 远小于 n​ 时非常高效。

大数据问题处理面试题:https://www.seven97.top/interview/intelligence-massivedata/massivedataprocessing.html

快速选择法(效率最优)

基于快速排序的 ​partition​ 操作。每次随机选择一个基准元素(pivot),将数组划分为比基准小和比基准大的两部分。根据基准的位置与k的关系,决定继续划分哪一部分

public ArrayList<Integer> GetLeastNumbers_Solution(int[] input, int k) {ArrayList<Integer> result = new ArrayList<>();if (input == null || k <= 0 || k > input.length) {return result;}quickSelect(input, 0, input.length - 1, k);for (int i = 0; i < k; i++) {result.add(input[i]);}return result;
}private void quickSelect(int[] arr, int left, int right, int k) {if (left >= right) return;int pivotIndex = partition(arr, left, right);if (pivotIndex == k - 1) { // 基准点正好是第k-1个位置(0-indexed),则其左边就是最小的k个数return;} else if (pivotIndex > k - 1) { // 基准点位置大于k-1,说明最小的k个数在左边quickSelect(arr, left, pivotIndex - 1, k);} else { // 基准点位置小于k-1,说明最小的k个数还需要包括右边的一部分quickSelect(arr, pivotIndex + 1, right, k);}
}// 分区操作,将小于基准的数放在左边,大于基准的数放在右边,返回基准的最终位置
private int partition(int[] arr, int left, int right) {// 随机选择基准,避免最坏情况int randomIndex = new Random().nextInt(right - left + 1) + left;swap(arr, left, randomIndex);int pivot = arr[left];int i = left, j = right;while (i < j) {while (i < j && arr[j] >= pivot) j--;while (i < j && arr[i] <= pivot) i++;if (i < j) swap(arr, i, j);}swap(arr, left, i);return i;
}private void swap(int[] arr, int i, int j) {int temp = arr[i];arr[i] = arr[j];arr[j] = temp;
}
  • 时间复杂度​:​平均 O(n)​。每次partition操作平均处理n个元素,但每次都能将问题规模大致减半(n + n/2 + n/4 + ... ≈ 2n)。最坏情况(每次选的基准都是最大或最小值)下为O(n²),但随机选择基准有效避免了最坏情况的发生。
  • 空间复杂度​:O(log n),递归调用栈的深度
http://www.wxhsa.cn/company.asp?id=766

相关文章:

  • 【初赛】时间复杂度 - Slayer
  • 微调
  • WPF 警惕 StylusPlugIn 的多线程安全问题
  • 【译】Visual Studio 八月更新已发布 —— 更智能的人工智能、更出色的调试功能以及更多控制权
  • RAG or 微调
  • 什么是AI CRM(人工智能客户关系管理)
  • 完整教程:WPF WriteableBitmap 高性能双缓冲图片显示方案
  • PHP 性能优化实战 OPcache + FPM 极限优化配置
  • 多校 3 - 1001. 求和
  • cache的基本原理
  • 【办公自动化】如何使用Python脚本自动化处理音频?
  • 如何用 vxe-table 实现2个树表格可以互相拖拽数据
  • CSP 初赛必背
  • 最新安卓版16音轨简谱编辑器软件使用说明
  • 【URP】Unity超分辨率优化实践
  • 0125_命令模式(Command)
  • 通过 GitHub 仓库下载微信 Mac Windows 历史版本(Rodert 提供)
  • CSP 初赛整理
  • 使用GoLang执行Shellcode的技术解析
  • 【GitHub每日速递】想提升技术?这 些开源项目涵盖编程、服务器管理,别错过
  • cidr Not Available
  • 读人形机器人08制造行业
  • 现代Web应用渗透测试:JWT攻击实战指南
  • 分享10 个百度资源网盘搜索的网站大全
  • RST报文段的意义
  • Delphi TStringGrid控件学习笔记
  • 你可能不需要WebSocket-服务器发送事件的简单力量
  • JS 定时器 点击简书 button 加载更多 控制台触发
  • Oops! internal error 1341 occurred.
  • navicat查看mysql数据库大小