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

二分答案之第 K 小/大

参考资料来源灵神在力扣所发的题单,仅供分享学习笔记和记录,无商业用途。

核心思路:

设置check函数和采用哪种模型

  • 第 k 小等价于:求最小的 x使用01模型,满足条件并且 ≤答案 的数至少有 k 个。
  • 第 k 大等价于:求最大的 x使用10模型,满足条件并且≥答案 的数至少有 k 个。

证明:

第k小为什么满足条件并且 ≤答案 的数至少有 k 个?

表明了当前答案前面还有至少k个数,而答案越小答案前面的数就越小具有单调性。采用二分01模型当区间内的数满足条件并且 ≤答案 的数恰好有 k 个,会默认收敛至第一个。

力扣题单练习(灵神题单中摘取题目)

668. 乘法表中第k小的数

题意:

给定m x n 的一个整数矩阵,其中 mat[i][j] == i * j(下标从 1 开始)。找到并返回第k小的数字

思路:

合理右边界:选取最后一个元素

检查函数:枚举每一行,判断当前行有多少小于等于二分答案的数

因为生成的数等于行*列,所以答案/行=列,而列如果有小数说明当前行没有答案数,只有小于它的数(列)

如果答案/行>n,说明当前行里所有元素都小于二分答案。

统计有多少个小于等于答案的数,大于等于k:缩小答案,反之亦然。

为什么二分结束后,答案 ans 一定在乘法表中?

采用01模型可以确定在当二分区间内的所有数都能使cnt>=k,但是除了这个区间的第一位,其他都不是乘法表中的数。

有小数在计算过程中忽略了转换成个数,只有第一位满足条件。

class Solution {
public://检查函数:枚举每一行,判断当前行有多少小于等于二分答案的数//因为生成的数等于行*列,所以答案/行=列,而列如果有小数说明当前行没有答案数,只有小于它的数(列)//如果答案/行>n,说明当前行里所有元素都小于二分答案。//统计有多少个小于等于答案的数,大于等于k:缩小答案,反之亦然。//为什么二分结束后,答案 ans 一定在乘法表中?//采用01模型可以确定在当二分区间内的所有数都能使cnt>=k,但是除了这个区间的第一位,其他都不是乘法表中的数。//有小数在计算过程中忽略了转换成个数,只有第一位满足条件。bool check(int m, int n, int k, int buff){int cnt=0;for(int i=1;i<=m;i++) cnt+=min(buff/i,n);return cnt>=k;}int findKthNumber(int m, int n, int k) {//题意:给定m x n 的一个整数矩阵,其中 mat[i][j] == i * j(下标从 1 开始)。找到并返回第k小的数字//合理右边界:选取最后一个元素int l=1,r=m*n,mid;while(l<r){mid=l+((r-l)>>1);if(check(m,n,k,mid)) r=mid;else l=mid+1;}return l;}
};

378. 有序矩阵中第 K 小的元素

题意:有序矩阵(每行每列升序)中找第k小元素

思路:

二分范围:矩阵最小值(左上角)到最大值(右下角)

检查函数:统计矩阵中≤x的元素数量(利用矩阵有序性优化)

返回值:如果数量≥k,说明第k小元素≤x,需继续向左搜索

class Solution {
public:// 检查函数:统计矩阵中≤x的元素数量(利用矩阵有序性优化)// 返回值:如果数量≥k,说明第k小元素≤x,需继续向左搜索bool check(vector<vector<int>>& matrix, int k, int x) {int cnt = 0;int n = matrix.size();int i = n - 1, j = 0; // 从左下角开始遍历while (i >= 0 && j < n) {if (matrix[i][j] <= x) {cnt += i + 1; // 当前列上方所有元素均≤xj++; // 右移} else {i--; // 上移}}return cnt >= k;}int kthSmallest(vector<vector<int>>& matrix, int k) {// 题意:有序矩阵(每行每列升序)中找第k小元素// 二分范围:矩阵最小值(左上角)到最大值(右下角)int n = matrix.size();int l = matrix[0][0], r = matrix[n-1][n-1];while (l < r) {int mid = l + (r - l) / 2;if (check(matrix, k, mid)) {r = mid; } else {l = mid + 1; }}return l; }
};

719. 找出第 K 小的数对距离

题意:给定一个整数数组和整数,要求返回所有所有数对距离中第k小的数对距离。数对距离定义为a和b的绝对差值。

思路:

核心:二分查找可能的距离 + 滑动窗口统计数量

合理右边界:采用整数数组中绝对差值最大的数对距离

检查函数:统计数组中“距离≤x”的数对数量(利用排序+滑动窗口优化)

原理:数组已排序,固定右边界j,寻找最小左边界i使得nums[j]-nums[i]≤x

因为想要绝对差值越小就需要下标越来越靠近。

此时[j-i]个对数((i,j),(i+1,j),...,j-1,j)均满足距离≤x

返回值:若数量≥k,说明第k小的距离≤x,需向左收缩二分范围

class Solution {
public:// 检查函数:统计数组中“距离≤x”的数对数量(利用排序+滑动窗口优化)// 原理:数组已排序,固定右边界j,寻找最小左边界i使得nums[j]-nums[i]≤x//因为想要绝对差值越小就需要下标越来越靠近。// 此时[j-i]个对数((i,j),(i+1,j),...,j-1,j)均满足距离≤x// 返回值:若数量≥k,说明第k小的距离≤x,需向左收缩二分范围bool check(vector<int>& nums, int k, int x){int cnt = 0, i = 0;for (int j = 0; j < nums.size(); j++) {while (nums[j] - nums[i] > x) i++;  cnt += j - i; }return cnt >= k;}int smallestDistancePair(vector<int>& nums, int k) {//题意:给定一个整数数组和整数,要求返回所有所有数对距离中第k小的数对距离。数对距离定义为a和b的绝对差值。//核心:二分查找可能的距离 + 滑动窗口统计数量//合理右边界:采用整数数组中绝对差值最大的数对距离sort(nums.begin(),nums.end());int l=0,r=nums.back()-nums[0],mid;while(l<r){mid=l+((r-l)>>1);if(check(nums,k,mid)) r=mid;else l=mid+1;}return l;}
};

878. 第 N 个神奇数字

思路:

合理右边界:a和b取最小乘n,这样能保证至少有n个神奇的数字

检查函数:使用数学公式计算[1..x]中能被a或b整除的数的个数

最大公约数:分解两个数为质因数乘积,找出最大值 “公共质因数”(两个数都包含的质因数);

最小公倍数:能同时被这些整数整除的最小正整数。等于a*b/最大公约数

容斥原理:|A|:1~x 中 “能被 a 整除” 的数(如 a=2 时,A={2,4,6,...};

|B|:1~x 中 “能被 b 整除” 的数(如 b=3 时,B={3,6,9,...};

计算|A ∪ B|(能被 a 或 b 整除的数的总个数),计算|A ∩ B|(既能被 a 整除、又能被 b 整除的数的个数)=x/a和b的最小公倍数

|A ∪ B|=|A|+|B|-|A ∩ B|(A和B重合部分,减去冗余)

class Solution {
public:// 计算最大公约数:分解两个数为质因数乘积,找出最大值 “公共质因数”(两个数都包含的质因数);long long gcd(long long a, long long b) {return b == 0 ? a : gcd(b, a % b);}// 计算最小公倍数:能同时被这些整数整除的最小正整数。等于a*b/最大公约数long long lcm(long long a, long long b) {return a * b / gcd(a, b);}//检查函数:使用数学公式计算[1..x]中能被a或b整除的数的个数//容斥原理:|A|:1~x 中 “能被 a 整除” 的数(如 a=2 时,A={2,4,6,...});//|B|:1~x 中 “能被 b 整除” 的数(如 b=3 时,B={3,6,9,...});//计算|A ∪ B|(能被 a 或 b 整除的数的总个数),计算|A ∩ B|(既能被 a 整除、又能被 b 整除的数的个数)=x/a和b的最小公倍数//|A ∪ B|=|A|+|B|-|A ∩ B|(A和B重合部分,减去冗余)bool check(int n, int a, int b, long long x) {long long ab = lcm(a, b);long long cnt = x/a + x/b - x/ab;return cnt >= n;}int nthMagicalNumber(int n, int a, int b) {//合理右边界:a和b取最小乘n,这样能保证至少有n个神奇的数字const long long MOD = 1e9 + 7;long long l = min(a, b);long long r = (long long)min(a, b) * n; // 避免整数溢出long long mid;while (l < r) {mid=l+((r-l)>>1);if(check(n,a,b,mid)) r=mid;else l=mid+1;}return l % MOD; }
};

1201. 丑数 III

思路:

合理右边界:在a,b,c中取最小乘n,这样能保证至少有n个丑数

容斥原理: “先包容所有元素,再排斥重复计算的部分,最后补回多排斥的部分”

|A ∪ B ∪ C|(满足条件的数量)= |A| + |B| + |C| - |A∩B| - |A∩C| - |B∩C| + |A∩B∩C|

class Solution {
public:long long gcd(long long a, long long b){return b==0?a:gcd(b,a%b);}long long lcm(long long a, long long b){return a*b/gcd(a,b);}//容斥原理: “先包容所有元素,再排斥重复计算的部分,最后补回多排斥的部分”//|A ∪ B ∪ C|(满足条件的数量)= |A| + |B| + |C| - |A∩B| - |A∩C| - |B∩C| + |A∩B∩C|bool check(int n, int a, int b, int c, long long x){long long a_b=lcm(a,b);long long a_c=lcm(a,c);long long b_c=lcm(b,c);long long cnt=x/a + x/b + x/c - x/a_b - x/a_c - x/b_c + x/lcm(lcm(a,b),c);return cnt>=n;}int nthUglyNumber(int n, int a, int b, int c) {//合理右边界:在a,b,c中取最小乘n,这样能保证至少有n个丑数long long l=min(min(a,b),c),r=min(min(a,b),c)*n,mid;while(l<r){mid=l+((r-l)>>1);if(check(n,a,b,c,mid)) r=mid;else l=mid+1;}return l;}
};

力扣灵神题单2000分以下二分答案之第 K 小/大已完成,后续会更新2000+的专题。

http://www.dtcms.com/a/286932.html

相关文章:

  • CMake指令:常见内置命令行工具( CMake -E )
  • 乙烯丙烯酸酯橡胶市场报告:性能优势、行业现状与发展前景​
  • selenium后续!!
  • 【数据集】1970-2023年全球温室气体排放 GHG 数据集 EDGAR
  • 语音直播和视频直播的测试要点
  • 【ROS1】06-ROS通信机制——话题通信
  • OOA、OOD 与 OOP:面向对象范式的核心支柱详解
  • 接口测试的原则、用例与流程详解
  • ModelSim 配合 Makefile 搭建 Verilog 仿真工程
  • Docker-下载和安装
  • ADVB协议内容分析
  • LeetCode Hot100【6. Z 字形变换】
  • GI6E 加密GRID電碼通信SHELLCODE載入
  • CCF编程能力等级认证GESP—C++3级—20250628
  • 操作系统-处理机调度和死锁进程同步
  • 基于Qwen2.5-3B-Instruct的LoRA微调与推理实战指南
  • 多线程-3-线程同步
  • HTTPie: 开发者友好的http客户端工具
  • 数据排序
  • 特种作业操作证(制冷空调)的考试科目有哪些?
  • Xilinx Zynq:一款适用于软件定义无线电的现代片上系统
  • 使用 C# 实现移动加权平均(Weighted Moving Average)算法
  • java基础-5 : 面向对象
  • python网络爬虫(第三章/共三章:驱动浏览器窗口界面,网页元素定位,模拟用户交互(输入操作、点击操作、文件上传),浏览器窗口切换,循环爬取存储)
  • RPG60.生成可拾取物品
  • 拓扑排序/
  • 安卓Android项目 报错:系统找不到指定文件
  • Python编程:从入门到实践
  • rpa机器人流程自动化软件公司是做什么的?如何选择RPA厂商?简要介绍RPA技术、应用场景和未来趋势
  • Shell变量操作