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

第k个数字

第K个数

给定一个长度为 n 的整数数列,以及一个整数 k,请用快速选择算法求出数列从小到大排序后的第 k 个数。

所用方法和基本原理

快速选择算法是基于快速排序思想的一种选择算法。其基本原理如下:

  1. 划分操作:选择一个基准元素(这里选择数组中间位置的元素),通过双指针法将数组分为两部分,使得左边部分的元素都小于等于基准元素,右边部分的元素都大于等于基准元素。
  2. 确定目标位置:统计左边部分的元素个数。如果 k 小于等于左边部分元素个数,说明第 k 小的数在左边部分,继续在左边部分进行快速选择;否则,第 k 小的数在右边部分,需要在右边部分进行快速选择,并且此时 k 的值要减去左边部分的元素个数。
  3. 递归求解:不断重复上述过程,直到找到第 k 小的数。

代码及注释

public class QuickSelect {// 快速选择函数,用于找到数组中第k小的数public static int quickSort(int[] arr, int l, int r, int k) {// 如果左右指针重合,说明只有一个元素,直接返回该元素if (l == r) return arr[l];// 选择中间位置的元素作为基准元素xint x = arr[l + (r - l >> 1)];// 初始化左指针i,指向数组起始位置的前一个int i = l - 1;// 初始化右指针j,指向数组末尾位置的后一个int j = r + 1;// 当左指针小于右指针时,继续循环进行划分while (i < j) {// 从左向右移动左指针,找到第一个大于等于基准元素的位置while (arr[++i] < x);// 从右向左移动右指针,找到第一个小于等于基准元素的位置while (arr[--j] > x);// 如果左指针仍小于右指针,说明两个指针未相遇,交换两个指针所指向的元素if (i < j) {int tmp = arr[i];arr[i] = arr[j];arr[j] = tmp;}}// 计算左边部分(包括基准元素所在位置)的元素个数int lNum = j - l + 1;// 如果k大于左边部分元素个数,说明第k小的数在右边部分,递归在右边部分查找if (k > lNum) {return quickSort(arr, j + 1, r, k - lNum);} else {// 否则,第k小的数在左边部分,递归在左边部分查找return quickSort(arr, l, j, k);}}
}

举例说明

假设有数组 arr = [3, 2, 1, 5, 6, 4],要找第 3 小的数(即 k = 3)。

  1. 第一次划分
    • 选择基准元素 x = arr[2] = 1(中间位置)。
    • 初始化 i = -1j = 6
    • 移动 i 到位置 0(因为 arr[0] = 3 > 1),移动 j 到位置 5(因为 arr[5] = 4 > 1)。
    • 交换 arr[0]arr[5],数组变为 [4, 2, 1, 5, 6, 3]
    • 继续移动 i 到位置 1(因为 arr[1] = 2 > 1),移动 j 到位置 2(因为 arr[2] = 1)。
    • 此时 i = 1j = 2,左边部分元素个数 lNum = 2 - 0 + 1 = 3
    • 因为 k = 3,所以第 k 小的数在左边部分,继续在左边部分 [4, 2, 1] 进行查找。
  2. 第二次划分
    • 选择基准元素 x = arr[1] = 2(中间位置)。
    • 初始化 i = -1j = 2
    • 移动 i 到位置 0(因为 arr[0] = 4 > 2),移动 j 到位置 1(因为 arr[1] = 2)。
    • 交换 arr[0]arr[1],数组变为 [2, 4, 1]
    • 继续移动 i 到位置 1(因为 arr[1] = 4 > 2),移动 j 到位置 0(因为 arr[0] = 2)。
    • 此时 i = 1j = 0,左边部分元素个数 lNum = 0 - 0 + 1 = 1
    • 因为 k = 3k > lNum,所以第 k 小的数在右边部分 [4, 1] 进行查找,并且 k 变为 3 - 1 = 2
  3. 第三次划分
    • 选择基准元素 x = arr[1] = 1(中间位置)。
    • 初始化 i = -1j = 1
    • 移动 i 到位置 0(因为 arr[0] = 4 > 1),移动 j 到位置 1(因为 arr[1] = 1)。
    • 交换 arr[0]arr[1],数组变为 [1, 4]
    • 继续移动 i 到位置 0(因为 arr[0] = 1),移动 j 到位置 0(因为 arr[0] = 1)。
    • 此时 i = 0j = 0,左边部分元素个数 lNum = 0 - 0 + 1 = 1
    • 因为 k = 2k > lNum,所以第 k 小的数在右边部分 [4] 进行查找,并且 k 变为 2 - 1 = 1
  4. 第四次划分
    • 此时数组只有一个元素 [4]l = r = 0,直接返回 arr[0] = 4,找到第 3 小的数为 4。

方法的优劣

  1. 时间复杂度
    • 平均情况:快速选择算法平均时间复杂度为 O ( n ) O(n) O(n),因为每次划分平均可以排除一半的数据。
    • 最坏情况:时间复杂度为 O ( n 2 ) O(n^2) O(n2),当每次选择的基准元素都是数组中的最大或最小元素时,划分会极不均匀,导致每次只能排除一个元素。
  2. 空间复杂度
    • 平均情况:空间复杂度为 O ( log ⁡ n ) O(\log n) O(logn),这是由于递归调用栈的深度平均为 log ⁡ n \log n logn
    • 最坏情况:空间复杂度为 O ( n ) O(n) O(n),在最坏情况下,递归调用栈的深度为 n n n

优点:平均情况下时间复杂度为线性,效率较高,适用于大规模数据的选择问题。
缺点:最坏情况下时间复杂度退化到 O ( n 2 ) O(n^2) O(n2),并且算法不稳定,即相同元素的相对顺序在排序前后可能会改变。

http://www.lqws.cn/news/518041.html

相关文章:

  • 归并排序算法
  • 企业内部安全组网技术解析:安全通道选型、零信任架构与数据合规加密防护
  • 计算机网络-----详解HTTP协议
  • 基于springboot+vue的智慧农业专家远程指导系统
  • 苹果签名应用掉签频繁原因排查,以及如何避免
  • Mysql使用窗口函数查询
  • 左神算法之有序二维矩阵中的目标值查找
  • vscode管理go多个版本
  • 英飞凌高性能BMS解决方案助力汽车电动化
  • 【世纪龙科技】新能源汽车VR虚拟体验展示馆-解锁认知新维度
  • 灰度发布怎么保证数据库一致的
  • AES加密:为你的PDF文档加上一道钢铁防线
  • Kubernetes、Docker Swarm 与 Nomad 容器编排方案深度对比与选型指导
  • 论文阅读:A Survey on Large Language Models for Code Generation
  • 不用vue,只用html,即可简单实现electron项目
  • 鸿蒙OpenHarmony[Disassembler反汇编工具]ArkTS运编译工具链
  • IntelliJ IDEA 社区版安装终极教程(2025 最新图文详解)
  • 微信小程序中scss、ts、wxml
  • React19源码系列之 API (react)
  • 惠普HP Laser MFP 116w 打印机信息
  • 深度解析Lucene IndexWriter 性能优化
  • 银河麒麟高级服务器操作系统(全架构)OpenGauss 数据库部署手册
  • Fisco Bcos学习 - 控制台搭建和基本使用
  • SpringBoot中5种拦截器使用场景
  • Odoo OWL 前端开发:ORM 与 RPC 服务的选择
  • HarmonyOS 5分布式数据库有哪些性能指标?
  • GPT-5企业级应用落地指南:70个工业场景实战部署全景(2025)
  • 贪心算法理论与实践总结
  • 《中国电信运营商骨干网:历史、现状与未来演进》系列 第五篇:新玩家入局——中国广电CBNNET如何构建全国一张网?
  • 鸿蒙系统(HarmonyOS)经典红色风格登录页布局