因为矩阵中某一个元素下方和右边的数字比它大,所以在搜索的时候可以使用类似 Prim 算法的方式,在搜索的结果边缘进行搜索,然而算法超时。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 class Solution {public : int kthSmallest (vector<vector<int >>& matrix, int k) { int m=matrix.size (); int n=matrix[0 ].size (); set<pair<int ,int >> bag; pair<int ,int > minPos=make_pair (0 ,0 ); bag.insert (minPos); for (int i=2 ;i<=k;i++){ int minNum=INT_MAX; for (auto it=bag.begin ();it!=bag.end ();it++){ pair<int ,int > tPos=*(it); pair<int ,int > newPos1=make_pair (tPos.first+1 ,tPos.second); pair<int ,int > newPos2=make_pair (tPos.first,tPos.second+1 ); if ((bag.find (newPos1)!=bag.end () || tPos.first==m-1 ) && (bag.find (newPos2)!=bag.end () || tPos.second==n-1 )){ bag.erase (it); continue ; } if (bag.find (newPos1)==bag.end () && (*it).first<m-1 && matrix[newPos1.first][newPos1.second]<minNum){ minPos=newPos1; minNum=matrix[newPos1.first][newPos1.second]; } if (bag.find (newPos2)==bag.end () && (*it).second<n-1 && matrix[newPos2.first][newPos2.second]<minNum){ minPos=newPos2; minNum=matrix[newPos2.first][newPos2.second]; } } bag.insert (minPos); } return matrix[minPos.first][minPos.second]; } };
放弃复杂的尝试,使用优先级队列(堆)来实现也是一个比较方便的办法(可以通过剪枝优化一些时间复杂度)。但是这里几乎没有好好利用矩阵的性质。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 class Solution {public : int kthSmallest (vector<vector<int >>& matrix, int k) { priority_queue<int > pq; for (int i=0 ;i<matrix.size ();i++){ for (int j=0 ;j<matrix[i].size ();j++){ if (pq.size ()==k && matrix[i][j]>pq.top ()) break ; pq.emplace (matrix[i][j]); if (pq.size ()>k) pq.pop (); } } return pq.top (); } };
发现 Discuss 中的二分法解法也很妙。
这里的二分法和常用在排序数组中的二分法有些不同,这里的二分是取最小和最大的数字(分别位于矩阵的左上角和右下角),然后将这两个数字进行二分,查找矩阵中小于该数字的元素的个数,如果元素的个数小于 k,那就说明要找的第 k 个元素在二分数字的右边,否则它就在二分数字的前面。这样就可以进一步缩小扫描的区间,最终将区域定位在要找的数字上。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class Solution {public : int kthSmallest (vector<vector<int >>& matrix, int k) { int m=matrix.size (),n=matrix[0 ].size (); int start=matrix[0 ][0 ]; int end=matrix[m-1 ][n-1 ]; while (end>=start){ int mid=start+(end-start)/2 ; int count=0 ; for (int i=0 ;i<m;i++){ for (int j=0 ;j<n;j++){ if (matrix[i][j]<=mid) count++; else break ; } } if (count<k) start=mid+1 ; else end=mid-1 ; } return start; } };
最后,还能使用Leetcode 240. Search a 2D Matrix II 中的思路(从左下角开始比较)优化查找 mid 的时间复杂度(优化到对数级别)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 class Solution {public : int kthSmallest (vector<vector<int >>& matrix, int k) { int m=matrix.size (),n=matrix[0 ].size (); int start=matrix[0 ][0 ]; int end=matrix[m-1 ][n-1 ]; while (end>=start){ int mid=start+(end-start)/2 ; int count=0 ; int i=m-1 ,j=0 ; while (i>=0 && j<n){ if (matrix[i][j]<=mid){ j++; count+=i+1 ; }else { i--; } } if (count<k) start=mid+1 ; else end=mid-1 ; } return start; } };