1. 程式人生 > >Leetcode--378. 有序矩陣中第K小的元素

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;
    }
};

    看了好幾遍終於懂了一點,抓緊把理解的寫下來。最近感覺好累,最近的事情太多了。