• 【LeetCode】有序矩阵中第 K 小的元素 [M](二分)


    378. 有序矩阵中第 K 小的元素 - 力扣(LeetCode)

    一、题目

    给你一个 n x n 矩阵 matrix ,其中每行和每列元素均按升序排序,找到矩阵中第 k 小的元素。
    请注意,它是 排序后 的第 k 小元素,而不是第 k 个 不同 的元素。

    你必须找到一个内存复杂度优于 O(n2) 的解决方案。

    示例 1:
    输入:matrix = [[1,5,9],[10,11,13],[12,13,15]], k = 8
    输出:13
    解释:矩阵中的元素为 [1,5,9,10,11,12,13,13,15],第 8 小元素是 13

    示例 2:
    输入:matrix = [[-5]], k = 1
    输出:-5

    提示:

    • n == matrix.length
    • n == matrix[i].length
    • 1 <= n <= 300
    • -109 <= matrix[i][j] <= 109
    • 题目数据 保证 matrix 中的所有行和列都按 非递减顺序 排列
    • 1 <= k <= n2

    二、代码

    1. class Solution {
    2. public int kthSmallest(int[][] matrix, int k) {
    3. // 矩阵行数
    4. int n = matrix.length;
    5. // 矩阵列数
    6. int m = matrix[0].length;
    7. // 有序矩阵的最小值,即矩阵左上角的值
    8. int left = matrix[0][0];
    9. // 有序矩阵的最大值,即矩阵右下角的值
    10. int right = matrix[n - 1][m - 1];
    11. // 矩阵中第k小的数
    12. int ans = 0;
    13. // 记录小于等于mid这个数的信息,包含小于等于mid的数有多少个和小于等于mid并且最接近mid的数是什么
    14. Info equalOrLessMidInfo = null;
    15. // 利用二分法,找到小于等于mid的数正好有k个的信息
    16. while (left <= right) {
    17. // 二分
    18. int mid = left + ((right - left) >> 1);
    19. // 获取矩阵中小于等于mid的Info信息,包括小于等于mid的个数,和小于等于mid的数中最接近mid的数是什么
    20. equalOrLessMidInfo = getequalOrLessNumInfo(matrix, mid);
    21. // 如果小于等于mid的数少于k个,意味着我们还需要提高mid的值
    22. if (equalOrLessMidInfo.equalOrLessNumCnt < k) {
    23. // 取右半部分
    24. left = mid + 1;
    25. // 这个分支需要注意:
    26. // 如果小于等于mid的数大于k个,意味着我们还需要减少mid的值,尝试找到正好等于k个的值
    27. // 但是一定存在一个mid,正好使得矩阵中小于等于它的数有k个吗?并不一定,因为矩阵中可能存在重复的数。
    28. // 假设要找第7小的数,矩阵中从小到大排列出来是1、2、3、4、5、5、5、5、6、7
    29. // 正确答案应该是5,5是这个矩阵中第7小的数,但是在这个代码中是无法找到一个mid使小于等于它的数有7个的
    30. // 比如如果找小于等于5,最后的结果就是8个,如果找小于等于6,结果就是9个,小于等于4,结果就是4个
    31. // 所以并不能保证一定能找到equalOrLessMidInfo.equalOrLessNumCnt == k的结果,有可能equalOrLessMidInfo.equalOrLessNumCnt > k,但是最终的结果就在这个equalOrLessMidInfo中。
    32. // 所以,我们需要在等于k和大于k的时候都去记录一下ans,因为这个时候的equalOrLessMidInfo.nearNum都有可能是答案
    33. // 不用记录equalOrLessMidInfo.equalOrLessNumCnt < k的情况,因为如果此时小于等于mid的数都不够k个,就不可能找到矩阵中第k小的数,答案一定不在这种情况中。
    34. } else if (equalOrLessMidInfo.equalOrLessNumCnt >= k) {
    35. // 如果存在小于等于Mid的数正好有k个,那么此时的距离mid最近的数一定就是答案了,就算是继续执行这个while循环,也永远不会进入到这个分支了,因为这里会取左半部分,会降低下一轮mid的值,那么必然就会使小于mid的值变得小于k
    36. // 如果如果存在小于等于Mid的数正好大于k个,就会继续执行循环,找更小的mid来尝试能不能找到更接近k个的解。但是同时也会将此时的最接近mid的值作为临时的答案,如果后面的循环再也进不到这个分支了,那是因为后面的都不足k个了,答案不可能在不足k个的情况里,所以这一次记录的临时答案就是最终的答案,这个答案在矩阵中肯定是存在相同的值的
    37. ans = equalOrLessMidInfo.nearNum;
    38. // 取左半部分
    39. right = mid - 1;
    40. }
    41. }
    42. return ans;
    43. }
    44. public class Info {
    45. // 小于等于num并且最接近num的数是多少
    46. int nearNum;
    47. // 小于等于num的数一共有多少个
    48. int equalOrLessNumCnt;
    49. public Info(int nearNum, int equalOrLessNumCnt) {
    50. this.nearNum = nearNum;
    51. this.equalOrLessNumCnt = equalOrLessNumCnt;
    52. }
    53. }
    54. // 找到矩阵中小于等于num的数有多少个,并且小于等于num且最接近它的数是多少
    55. public Info getequalOrLessNumInfo(int[][] matrix, int num) {
    56. // 从右上角开始
    57. int row = 0;
    58. int col = matrix[0].length - 1;
    59. // 记录小于等于num的个数
    60. int cnt = 0;
    61. // 记录小于等于num并且最接近num的数是多少
    62. int ans = Integer.MIN_VALUE;
    63. while (row <= matrix[0].length - 1 && col >= 0) {
    64. // if (matrix[row][col] == num) {
    65. // ans = matrix[row][col];
    66. // cnt += (col + 1);
    67. // return new Info(ans, cnt);
    68. // }
    69. // 如果matrix[row][col] == num,有可能这个位置就是答案点,也有可能它的下一行还会有符合要求的,所以记录一下临时答案,然后再去下一行看有没有小于等于num的数,如果有的话,就再加上这一部分才不会遗漏答案。因为这个矩阵只是列和行有序的,但是不同行不同列并不存在严格的有序性,所以还需要额外判断判断一下
    70. if (matrix[row][col] <= num) {
    71. // 如果找到的数可以推高ans的答案,让他更接近num,就更新
    72. ans = Math.max(ans, matrix[row][col]);
    73. // 当前行col列及其左边的数都满足小于等于num,所以累加到数量中去
    74. cnt += (col + 1);
    75. // 向下移动一行
    76. row++;
    77. // 如果当前数大于num
    78. } else if (matrix[row][col] > num) {
    79. // 就向左移动一个位置,去看左边的位置能不能小于等于num,因为左边都是比自己小的数
    80. col--;
    81. }
    82. }
    83. // 返回答案
    84. return new Info(ans, cnt);
    85. }
    86. }

    三、解题思路 

    整个流程就是求两个信息:

    1、小于等于某一个值的个数有几个。

    2、当找到小于等于某一个值的数的数量等于我们的目标数量时,就去求最接近它的是谁?

  • 相关阅读:
    MySQL5.5——安装及卸载干净方法(保姆级教学)
    Python入门到放弃
    解决 filezilla 连接服务器失败问题
    THREEJS 性能优化
    Nginx快速入门及配置文件结构
    Python将字符串转换成dataframe
    uniapp——上传图片获取到file对象而非临时地址——基础积累
    Mybatis Available parameters are [0, 1, param1, param2]解决方法
    jenkins 2.346.1 从git拉取后自动构建部署springboot maven项目
    如何使用 grep 跨多行查找模式匹配
  • 原文地址:https://blog.csdn.net/cy973071263/article/details/127812277