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

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

示例:

matrix = [
   [ 1,  5,  9],
   [10, 11, 13],
   [12, 13, 15]
],
k = 8,

返回 13。

说明: 
你可以假设 k 的值永远是有效的, 1 ≤ k ≤ n2 。

--------------------------------------------------------------------------------------------------------------------------------------------------

我通过这道题了解了,二分查找的算法怎么写,之前只在书上见过,并没有实际的应用过,今天是第一次看到怎么应用在题目中。。。。。。。

由于每行每列的方向都是递增的。 这也是一个二分写法的题目的一个特点,表内元素必须有序。

AC:

class Solution {
public:
    bool fun(long long midd,vector<vector<int>>& matrix, long long k,long long n)
    {
        int sum=0;
        for (int i=0;i<n;i++)
        {
            int l=0;
            int r=n-1;
            int ans=0;          // ans是用来记录每行中比midd小的元素的数量。
         while(l<=r)            //闭区间不问空的条件是左界不大于右界,即(l<=r),如果是左闭右开区间,不空的条件是l<r
           {
            int mid=(l+r)/2;
            if (matrix[i][mid]<midd)
            {
                ans=mid+1;      //因为每行元素都是递增的,那么mid+1就表示在这一行中比midd小的元素的数量。
                l=mid+1;
            }
            else
                r=mid-1;
          }
            sum=sum+ans;
        }
      return k>sum;     //如果sum<k(即midd的排名比k小),说明midd猜的比较小。在kith那个函数中的左边界就要mid+1(即变                                                // 大)因为在矩阵中,越往右下角越大。
    }
    int kthSmallest(vector<vector<int>>& matrix, int k) {
        long long n=matrix.size();
        long long l=matrix[0][0];
        long long r=matrix[n-1][n-1];
        long long ans=0;
        while(l<=r)
        {
            long long mid=(l+r)/2;
            if (fun(mid,matrix,k,n))                       //mid与sum是一个正相关
            {
                ans=mid;
                l=mid+1;
            }
            else
            {
                r=mid-1;
            }
        }
        return (int)ans;
    }
};

如果用STL中的库函数的话,那么可以写成

  1. 两个迭代器相减代表的是两个之间的距离。
  2. lower_bound() 返回一个迭代器,指向第一个不小于目标val的元素。
  3. upper_bound()返回一个迭代器,指向第一个大于目标val的元素。

AC:

class Solution {
public:
    int kthSmallest(vector<vector<int>>& matrix, int k) {
        vector<vector<int>> mm=matrix;    //偷懒的写法,matrix觉得写的麻烦。
        int n=mm.size();
        long long l=mm[0][0];
        long long r=mm[n-1][n-1];
        long long mid=0;
        while(l<=r)
        {
            mid=(l+r)/2;    //写到这明白了为什么许多人写成 l+(r-l)/2  这样不容易溢出,如果直接(l+r)/2,当l=r=2000000000时

                                    //虽然这个时候l和r都没有超出int的界限,但是l+r 却溢出,导致RE,这时可以l,r,mid全改为long long 。
            int cnt1=0;
            int cnt2=0;
            for (int i=0;i<n;i++)
            {
                auto m=mm[i];
                cnt1=cnt1+lower_bound(m.begin(),m.end(),mid)-m.begin();  //upper_bound()  这两个迭代器相减的结果的数值是比                                                                                                                       //mid小的元素的个数
                cnt2=cnt2+upper_bound(m.begin(),m.end(),mid)-m.begin();  // upper_bound() 这两个迭代器相减的结果的数值是不                                                                                                                       //小于mid的元素个数。
            }
            if (k>cnt1&&k<=cnt2)
                return mid;
            if (k>cnt2)
                l=mid+1;
            else
                r=mid-1;
        }
        return mid;
    }
};

    看了好几遍终于懂了一点,抓紧把理解的写下来。最近感觉好累,最近的事情太多了。

猜你喜欢

转载自blog.csdn.net/weixin_41514525/article/details/82962164