剑指offer:二维数组的查找
问题:在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照
从上到下的顺序排序。请完成一个函数,输入这样的一个二维数组和整数,判断数组中
是否有该整数。
思路:
1.首先可以判断该问题最差的算法是O(n^2),所以比这个方法更差的方法就不用考虑了。
2.其次可以想到可以用到 广度优先搜索(BFS) 算法。
3.最后是书上提供的思路,让人拍案叫绝:
右上方的点具有很好的性质,它左边的点都比它小,它下边的点都比它大。
所以,如果 target < 该点,那么可以排除整个一列,如果 target > 该点,那么可以排除整个一行 的点。更好的是,排除一行(或一列)后的矩阵的右上方的点仍然具有该性质。
如图所示:
从矩阵中查找7.
实现
BFS
这个方法更加通用一些,已经在牛客网通过。
class Solution {
public:
struct Point
{
int row;
int column;
};
//建立队列Q
queue<Point> Q;
bool Find(int target, vector<vector<int> > array){
int row = array.size();
int column = array[0].size();
vector<vector<bool> > mark(row);
for (int i = 0; i < row; i++)
{
mark[i].resize(column);
}
for (int i = 0; i < row; i++){
for (int j = 0; j < column; j++){
mark[i][j] = false; //false 表示当前没有被扩展
}
}
//异常
if (row == 0|| column == 0) return false;
//比最小的小,比最大的大,则肯定没有
if (target < array[0][0] || target > array[row - 1][column - 1]) return false;
Point first;
first.row = 0;
first.column = 0;
Q.push(first);
mark[0][0] = true;
while(Q.empty() == false)
{
Point now = Q.front(); //Q.front()是将队列最前面的值赋给now,并没有剔除队列,还需要pop
Q.pop();
if (target == array[now.row][now.column]) return true;
else if (target < array[now.row][now.column]) continue; //从Q中剩下的继续进行扩展
else //可以向右向下进行扩展
{
int row_down = now.row + 1;
int column_right = now.column + 1;
Point now_right, now_down; //now_right,now_down表示当前的结点的右边和下边
if (column_right < column && mark[now.row][column_right] == false){
now_right.row = now.row;
now_right.column = column_right;
Q.push(now_right);
mark[now.row][column_right] = true;
}
if (row_down < row && mark[row_down][now.column] == false)
{
now_down.row = row_down;
now_down.column = now.column;
Q.push(now_down);
mark[row_down][now.column] = true;
}
}
}
return false;
}
};
注意:
1.值得关注的是如何使用vector建立二维数组,并且定义的时候要注意
vector<vector<int> > array
两个’>'之间要有一个空格。
2.还要注意的是要考虑各种极端情况,如矩阵的行和列都是1等。
书上的方法
在看懂前面的思路3的基础上,比较好实现,已经通过牛客网验证。
class Solution {
public:
bool Find(int target, vector<vector<int> > array){
int row = array.size();
int column = array[0].size();
//right_row, right_column 记录当前的右上角点的行列坐标。
int right_row = 0;
int right_column = column - 1;
while(right_row < row && right_column >= 0)
{
if (target == array[right_row][right_column]) return true;
else if (target < array[right_row][right_column])
{
right_column--;
}
else
{
right_row++;
}
}
return false;
}
};
注:同样的,位于左下角的点也具有这样的性质,当然也可以作为起始点。书上的思路是由简到难,当一个点位于矩阵中间的时候不好分析,但当位于边界的时候就好分析。