题目:
Given a
n x n
matrix where each of the rows and columns are sorted in ascending order, find thek
th smallest element in the matrix.
Note that it is thek
th smallest element in the sorted order, not thek
th distinct element.
Example:matrix = [ [ 1, 5, 9], [10, 11, 13], [12, 13, 15] ], k = 8, return 13.
Note: You may assume k is always valid, 1 ≤ k ≤ n2.
解释:
首先,这个数组左上角的一定是最小值,右下角的一定是最大值
一.看到排序问题首先想到的就是用二分查找做,剑指offer上面的题目是在这样的二维数组中查找一个数字,如果这个数字存在的话,返回true,不存在则返回false。
对于这个数组,可以发现的一个规律是:对于右上角的数字,它左边的数字,都小于它,它下边的数字都大于它。
所以我们猜测某个值guess
就是第k大的数,那么在数组中,我们需要找到比它小的数的个数,如果比它小的个数大于k个,那么就往前找,如果比它小的个数小于k个,那么就往后找(二分查找)。
对于一个数字,可以肯定的一点是它左边的数字都比它小,所以对于每个元素matrix[i][j]
,从右往左找到它这一行以及它下面所有行的第一个比它小的数,假设列号是[j]
那么这一行比它小的数就是j+1
个,最后每一元素的比它小的数都存在于它左边的一个左上角阶梯状的形状中。
不能直接用>0 <0 =0判断,因为==0的时候的guess
值有可能不在matrix中,所以注意right的更新,以及最后返回的是left。注意while
循环是<
不然最后可能跳不出循环。
二.然后这道题还可以用堆做:
python中只有最小堆,如果想用最大堆,先对所有元素取负号,最后再取负号恢复
把有可能成为最小的元素的值往里面放,放入的个数大于k个。后期可能一次性pop()
1.第一种解法,可以把所有的元素都放进堆中,然后pop k次得到最终结果,但是效率很低,没有用到矩阵本身排序的性质。
2.第二种解法,利用到矩阵本身的性质,不需要把所有的元素都放进去,从左上角开始放(因为左上角的值全局最小),只需要把最可能是最小值的元素进堆即可,把最左上角的元素和其索引进堆,对k进行遍历,每次弹出最小的元素,把该元素右边的元素进堆,如果该元素是第一列的元素,则把它下面的元素也进堆。
第一种解法,二分查找:
python代码:
class Solution:
def kthSmallest(self, matrix, k):
"""
:type matrix: List[List[int]]
:type k: int
:rtype: int
"""
n=len(matrix)
def possible(guess):
cnt=0
r,c=0,n-1
#对行遍历
while(r<n):
while(c>=0 and matrix[r][c]>guess):
c-=1
cnt+=c+1;
r+=1;
return cnt>=k
left=matrix[0][0]
right=matrix[n-1][n-1]
while (left<right):
mid=left+(right-left)//2
if possible(mid):
right=mid
else:
left=mid+1
return left
c++代码:
#include<vector>
using namespace std;
class Solution {
public:
int kthSmallest(vector<vector<int>>& matrix, int k) {
int n=matrix.size();
int left=matrix[0][0];
int right=matrix[n-1][n-1];
while(left<right)
{
int mid=left+(right-left)/2;
if(possible(matrix,mid,k))
{
right=mid;
}
else
{
left=mid+1;
}
}
return left;
}
bool possible(vector<vector<int>>& matrix,int guess,int k)
{
int cnt=0;
int n=matrix.size();
int r=0,c=n-1;
while(r<n)
{
while(c>=0 && matrix[r][c]>guess)
c--;
cnt+=c+1;
r++;
}
return cnt>=k;
}
};
利用堆的解法,python代码:
class Solution(object):
def kthSmallest(self, matrix, k):
"""
:type matrix: List[List[int]]
:type k: int
:rtype: int
"""
if not matrix:
return None
m,n=len(matrix),len(matrix[0])
nums=[(matrix[0][0],0,0)]
heapq.heapify(nums)
for _ in xrange(k):
val,i,j=heapq.heappop(nums)
if j==0 and i+1<m:
heapq.heappush(nums,(matrix[i+1][j],i+1,j))
if j+1<n:
heapq.heappush(nums,(matrix[i][j+1],i,j+1))
return val
总结
我觉得这道题目的经典解法还是二分查找,而且二分查找的速度真的很快。