leetcode有序矩阵中第K小的元素 + 二维矩阵搜索

有序矩阵中第K小的元素

题干描述

在这里插入图片描述在这里插入图片描述

IDEA

如果现在给定了矩阵中的某个数 x x x,如何快速找出在矩阵中小于等于 x x x的数有多少个呢?

  • 如图所示,可以考虑从最后一行出发,向右寻找第一个大于 x x x的数,则这个数的左侧都是小于等于 x x x的数

  • 在这个第一个大于 x x x的数向上走一步,到达上一行。显然此时该行该位置左侧的数都满足小于等于 x x x,但是右侧还可能具有小于等于 x x x的数,因为每一列自上到下是递增的。

    • 因此需要从此时的位置向右重复上面的步骤。
  • 在上述两步进行的同时计数

  • 以上过程重复进行,直到到达第一行。由于对于每一行都找到了正确的结果,因此很容易得出最后结果的正确性。

  • 因此考虑对区间[矩阵中的最小数,矩阵中的最大数]进行二分查找。

AC代码

class Solution {
    public int kthSmallest(int[][] matrix, int k) {
        int n = matrix[0].length;
        int low = matrix[0][0];
        int high = matrix[n - 1][n - 1];
        while(low < high)
        {
            int mid = low + (((high - low))>>1);
            int count = 0;
            int i = 0;    //当前列
            int cur_row = n - 1;    //当前行
            while(cur_row>=0)
            {
                while(i<n && matrix[cur_row][i] <= mid)
                    i++;   //在当前行往后走,直到碰到边界
                //实质上拐弯的元素才是需要的
                count+=i;
                cur_row--;
            }
            if(count < k)
                low = mid + 1;
            else high = mid;
        }
        return high;
    }
}

细节分析

  • 上述的代码十分美丽,但具有许多值得考究的细节
  • 这里mid = low + (((high - low))>>1),这和mid = (low + high)/2是等价的吗?
    • 不等价!
    • 例如low = -5,high = -4,前面式子计算结果为-5, 后面的计算结果为-4.说明/运算的计算结果是往0靠近的。
    • 而使用mid = low + (((high - low))>>1)保证了最终的计算结果总是往low的方向靠近不论结果的正负
  • 上述循环中的终止条件是什么(即返回的结果)?
    • 显然 l o w > h i g h low>high low>high l o w = = h i g h low == high low==high是两个首选
    • 事实上,只可能是 l o w = = h i g h low == high low==high
      • 考察退出这个循环前的最后一次循环,high比low多1
      • 此时由上面的分析,计算出来的 m i d mid mid是靠近low的,准确的说,在这最后一次循环中mid就等于low。
      • 考察最后的赋值方式,无论最后是对于low赋值还是对于high赋值, 最后循环退出的条件就是low == high;
  • 最重要的point:上述最后求出的high值(亦即Low值)一定存在于矩阵中吗?
    • 答案是一定存在!
    • 初始阶段,由于low指向矩阵中的最小元素,high指向矩阵中的最大元素,[low,high]区间中显然存在目标元素。
    • 使用分类讨论证明下面结论:无论如何以[low,high]为区间的元素里面包含目标值。
    • 假设某个时刻,low指向了我们要找的目标值,而high也等于我们要找的目标值时,结论显然成立。
    • 假设某个时刻,low指向了我们要找的目标值,而high大于我们要找的目标值时,进入循环:那么求出的mid一定是大于等于我们找到的目标值的,所以count的值一定大于等于k,于是会执行语句high = mid,此时的[low,high]即将范围缩小的区间包含目标值。
    • 假设在某个时刻,low小于我们要找的目标值,而high等于我们要找的目标值,进入循环。那么求出的mid一定是小于等于我们的目标值的。所以最后的count值一定会小于等于k。如果count的值小于k(即仅有小于k的元素满足小于等于mid),所以mid一定比目标值小,执行low = mid+1后[low,high]区间显然一定包含目标值。如果count的值等于k,说明刚好有k个元素满足小于等于mid,此时mid指向的值一定是我么要找的目标值,这是因为:
      • 由于原先high指向我们的目标值,因此这里的mid一定小于等于目标值。
      • mid不可能小于目标值。否则,按上述每行的统计过程,count一定小于k,这与count==k矛盾。
      • 所以此时算法令high = mid,[low,high]区间中一定包含目标值,因为至少high是目标值。
    • 假设在某个时刻,low小于我们要找的目标值,而high大于我们要找的目标值,进入循环。则根据求出的mid计算出来的count值所有情况都有可能满足。如果 c o u n t < k count < k count<k,则mid一定小于目标值,算法执行low = mid+1后显然[low,high]区间中包含目标值。而如果 c o u n t ≥ k count \geq k countk,分情况讨论下:
      • 如果 c o u n t = = k count == k count==k,那么mid的值一定大于等于目标值。否则如果mid小于目标值按上述按行统计的方法一定有 c o u n t < k count < k count<k,矛盾。所以此时令high = mid,由于mid大于目标值,[low,high]区间中存在目标值。
      • 如果 c o u n t > k count >k count>k,那么mid的值肯定是大于目标值的,否则 c o u n t < = k count<=k count<=k。所以此时令high = mid,由于mid大于目标值,最后求出的区间[low,high]中一定存在目标值。
    • 可以看到,综上所述,正确性的关键在于按行统计小于等于目标值的方式。即遇到第一个大于目标值的数,该行统计停止。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值