题目:
题解:
这里的二分法与常规的二分法有一些小区别:
- 左右两边的数字使用的是数组值而不是索引
- 每一次边界收缩判断的不是数值的大小,而是找到小于中位数的数字个数再与k相比
其实可以理解为,在某个数字的左上方向上的数字都比这个数字小,右下方的数字都比这个数字大,二分的上下界如果是5,15,那么二分的上下限就好比上图画的两个斜线(不严格以斜线为分割)。
package dudongdong;
public class MatrixKmin {
public static void main(String args[]) {
Integer m2[][] = {
{1,5,9},
{10,11,13},
{12,13,15}};
int k=8;
int n=3;
MatrixKmin mkm = new MatrixKmin();
System.out.println(mkm.kthSmallest(m2, k,n));
}
public int kthSmallest(Integer[][] m2,int k,int n) {
int l=m2[0][0];
int r=m2[n-1][n-1];
while(l<r) {
int mid = (l+r)/2;
System.out.println(l+" "+r+ " mid:"+mid);
//比中间值小的数
int count = noBiggerThanMid(m2,mid, n);
System.out.println("比中间值小的有:"+count+"个");
if(count < k ) {
// 向右收缩
l=mid+1;
} else {
// 向左收缩
r=mid;
}
}
return l;
}
public int noBiggerThanMid(Integer[][] m2,int mid,int n) {
//从下向上
int i = n-1;
//从左至右
int j = 0;
int count =0;
while(i>=0 && j<n) {
if(m2[i][j] <= mid) {
//此列全部小于mid
count += i+1;
// 向右寻找比mid小的数字
System.out.println("【向右寻找】第"+j+"列");
j++;
} else {
//向上寻找比mid小的数字
System.out.println("【向上寻找】 第"+i+"行");
i--;
}
}
return count;
}
}