剑指Offer.1
在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
解法
这道题还是有点难度,当然,想通了自然就不难了,画一个图就能很好观察如何快速查找。比方说我们要在如下矩阵中找到22。根据每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序特性,我们可以构造如下解决方案:
具体说来就是,不断缩小待搜索矩阵,最后将目标找到。Java实现如下:
/**
* Offer1
*
* 基本思路:矩阵截取,不断选取子矩阵
*/
public class Offer1 {
public static void main(String[] args) {
int target = 102;
int[][] array = {{1,2,4,6,8,10,11,13,15,17,20,22,24,27,30,32,35,36,38,39,42,44,46,47,48,49,51,54,55,56},{3,4,6,8,11,13,15,18,19,20,23,25,27,29,33,36,38,41,42,45,48,50,53,54,57,60,63,65,66,67},{5,7,8,11,14,15,17,19,21,23,24,28,31,33,36,39,41,44,47,49,52,53,56,57,60,63,66,68,71,72},{7,10,13,14,17,19,22,25,26,27,30,32,34,37,39,42,44,46,50,53,55,56,59,61,64,66,69,72,74,76},{8,12,16,17,20,21,23,26,29,31,33,35,37,40,42,45,48,49,52,55,58,59,61,63,67,69,70,74,76,79},{10,15,18,20,22,24,27,30,32,33,36,38,39,41,45,47,50,53,56,58,61,64,65,68,69,71,73,76,79,82},{13,17,20,23,24,26,28,31,34,36,38,41,42,43,48,50,53,56,58,61,64,67,68,71,73,74,75,78,81,84},{15,19,22,24,26,28,31,34,35,38,41,44,45,46,49,51,56,59,61,64,67,69,71,73,76,78,79,82,85,87},{17,20,23,26,29,30,32,37,40,42,43,47,50,53,55,56,58,62,64,66,69,72,75,78,80,81,83,86,88,90},{18,21,25,28,30,32,35,38,42,43,44,49,51,55,58,59,61,65,68,71,73,74,77,79,82,85,88,89,91,93},{20,23,28,29,33,36,37,40,43,45,47,52,55,58,60,62,63,66,70,74,76,77,79,81,85,88,89,91,93,94},{23,25,31,33,35,39,42,43,44,46,50,53,56,61,62,65,68,69,72,75,78,81,82,83,88,91,92,93,96,99},{26,27,33,34,38,40,45,48,51,53,55,56,58,64,66,69,72,75,77,80,82,84,87,88,90,93,94,95,99,101},{29,30,36,38,40,42,47,50,53,56,57,59,62,65,68,71,73,77,79,83,84,86,88,91,93,96,99,100,102,103},{32,33,39,42,44,46,49,53,56,59,62,65,68,71,72,75,77,78,80,85,87,90,92,94,96,99,101,103,105,107},{35,37,42,43,46,48,51,55,59,61,65,67,71,74,76,78,81,82,84,86,90,92,95,96,99,102,103,106,107,109},{36,39,43,46,49,50,53,58,62,65,67,70,73,76,77,79,84,87,88,90,93,96,99,102,103,106,108,111,112,115},{38,42,45,47,52,55,57,60,64,66,69,72,75,78,80,82,87,89,91,92,94,99,100,103,105,107,111,112,115,118},{39,44,48,49,55,57,60,63,66,69,72,75,78,80,82,85,89,92,94,95,98,101,102,105,108,111,112,115,116,120},{40,47,49,52,56,59,63,64,68,71,75,78,80,83,85,88,91,94,97,99,101,104,105,108,110,112,115,118,120,123},{42,50,53,55,59,62,66,67,71,73,78,81,82,86,87,90,94,97,100,101,104,106,107,109,111,114,117,120,123,126},{43,51,55,58,62,64,69,71,74,77,81,84,86,87,89,93,96,99,102,103,105,108,111,112,113,116,120,122,125,129},{45,54,56,59,65,67,72,75,76,79,84,87,90,92,94,97,99,102,104,107,108,111,114,116,119,120,122,124,127,132},{47,55,59,62,66,68,74,78,80,82,85,88,93,96,99,101,102,105,106,108,110,113,115,118,120,121,124,127,130,133},{49,56,61,65,68,70,76,81,83,85,87,91,95,99,102,103,104,106,108,110,113,116,118,121,124,127,130,133,134,137},{51,57,62,68,71,74,78,83,86,89,92,95,97,101,104,106,108,110,113,114,117,119,121,123,126,130,132,135,137,140},{53,59,65,69,73,75,81,86,88,92,95,97,100,103,107,109,111,112,115,117,120,122,125,126,129,131,133,138,141,143},{55,61,67,72,75,77,82,89,92,94,97,100,102,105,108,111,114,115,116,119,123,125,126,128,131,134,137,140,144,146},{57,63,68,74,78,81,85,90,95,98,100,101,103,107,110,114,117,119,120,123,126,129,130,133,136,138,141,144,146,149},{58,65,70,76,81,84,86,93,98,101,104,105,107,110,112,115,120,122,124,126,129,132,135,136,138,140,142,146,149,150}}; System.out.println(Find(target, array));
}
public static boolean FindHelper(int target, int[][] array, int m, int n, int starti, int startj) {
boolean result = false;
for (int j = startj; j < startj + n; j++) {
/* 先找是否相等 */
if(array[starti][j] == target){
result = true;
break;
}
/* 在非端点处找target区间 */
else if(j >= startj && j < startj + n - 1){
int endElem = array[starti][startj + n - 1];
if(array[starti][j] < target && target < array[starti][j + 1] || target > endElem){
if(target > endElem){
j = startj + n - 1;
}
/* 说明不可能在array[:][i + 1 ~ n]的位置了,只可能在array[:][0] ~ array[:][i]的位置 */
for (int i = starti; i < starti + m; i++) {
if(array[i][j] == target){
result = true;
break;
}
/* 在非上下端点处找target区间 */
else if(i >= starti && i < starti + m - 1){
if(array[i][j] < target && target < array[i + 1][j]){
//System.out.println("i:" + i + " j:" + j + " Value: " + array[i][j]);
/* 说明不可能在array[j][i + 1 ~ n]的位置了,只可能在array[j + 1][0] ~ array[:][i]的位置 */
int newM = array.length - (i + 1);
int newN = j + 1;
int newStarti = i + 1;
int newStartj = 0;
result = FindHelper(target, array, newM, newN, newStarti, newStartj);
return result;
}
}
}
break;
}
}
}
return result;
}
public static boolean Find(int target, int [][] array) {
boolean result = false;
int m = array.length;
int n = array[0].length;
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
System.out.print(array[i][j]);
System.out.print(" ");
}
System.out.println("");
}
result = FindHelper(target, array, m, n, 0, 0);
return result;
}
}
有几个特殊情况需要注意:
- 如果搜索行时,没有找到满足
array[starti][j] < target && target < array[starti][j + 1]
条件的j
,那么情况应该变为:(假设要在下面的矩阵中寻找102,即先在最后一列找101<102<103,然后直接截取黄色部分的矩阵继续搜索);
- 那么如果搜索列时没有找到满足
array[i][j] < target && target < array[i + 1][j]
的i
,那么target
要么小于这一列的最后一个元素,这样一来根据矩阵的特性,就没有解了;要么target
大于这一列的最后一个元素,但是,由于这个矩阵是我们已经截取的,也就是说,就算target
大,但是在外部也是无解的,所以不用对其做出处理。