search a 2d matrix
这道题乍一看很简单,其实还是有很多细节值得注意的。
首先说明,如果题目中是按照C语言的格式给出的matrix,也就是给出int m[][],再给出row和column,那么实际上我们可以将其作为一维数组 int m[row*column],来直接使用二分查找。复杂度是O(log(row*column))
从high level讲,程序的思路大致是这样:首先通过二分查找找到对应的row,再二分查找找到对应的column,所以复杂度应该是O(log(row)+log(column))= O(log(row*column))。所以实际上,这两种方式复杂度一样。
查找相应row的时候,我们的标准是:如果当前行(mid指向的行)最大的元素都比target要小,那么left = mid+1。如果当前行最小的元素都比target大,那么right = mid-1。如果target在当前行第一个和最后一个元素中间,那么我们认为找到这一行了。
其实这里是没有例外的。仔细想想看,当前行第一个元素和最后一个元素将整个实数分为了三个区域,那么target一定是位于这三个区域之中的,对不对?
有一种情况我们需要观察一下:那就是target比当前行(如第2行)第一个元素要小,比上一行(如第1行)最后一个元素要大。这种情况下,我们实际上不是在第2行进行处理的,而是通过right = mid-1转到第1行处理。而到第1行之后,由于target比第1行最后一个元素大,那么left = mid+1,将会造成left>right,从而跳出循环。
至于找到指定行之后如何找相应元素,就和普通的二分查找一样,这里不再赘述。
编程中需要注意的几个细节:
首先是二分法。使用二分的时候一定要注意,第一,循环中止的条件是left<=right,不然有可能忽略情况;第二,每次二分完毕之后,left=mid+1,而不是left=mid,注意体会。因为如果mid指向的元素不可能的话,我们下一次查找的时候应该从mid+1开始!
第二是关于矩阵操作。凡是给出矩阵的,第一不要定义xy,而一律用row和column来代替。原因很简单,xy和row/column完全是反着的,还要换算,非常麻烦。同时,如果用vector给出元素,坐标的极限记得要减去1!
class Solution
{
public:
bool searchMatrix(vector<vector<int> > &m, int target)
{
if (m.size() == 0)
return false;
int row = m.size() - 1;
int column = m[0].size() - 1;
int left = 0, right = row, mid = left + (right - left) / 2;
int rowFound = -1;
//!!!注意什么时候小于,什么时候小于等于
while (left <= right)
{
mid = left + (right - left) / 2;
if (m[mid][0] <= target && m[mid][column] >= target)
{
rowFound = mid;
break;
}
else if (m[mid][column] < target)
left = mid + 1;
else if (m[mid][0] > target)
right = mid - 1;
}
if (rowFound < 0)
return false;
//find the element in the corresponding row
left = 0;
right = column;
while (left <= right)
{
mid = left + (right - left) / 2;
if (m[rowFound][mid] == target)
return true;
else if (m[rowFound][mid] < target)
left = mid + 1;
else
right = mid - 1;
}
return false;
}
};
NOTE: Although the following code work, but it has a few bugs and is not very efficient
class Solution
{
public:
bool searchMatrix(vector<vector<int> > &m, int target)
{
if(m.size() == 0)
return false;
int row = m.size()-1;
int column = m[0].size()-1;
int left = 0, right = row, mid = left + (right - left) / 2;
bool rowFound = false;
//!!!注意什么时候小于,什么时候小于等于
while (left <= right)
{
mid = left + (right - left) / 2;
if (m[mid][0] <= target && m[mid][column] >= target)
{
rowFound = true;
break;
}
else if (m[mid][column] < target)
left++;
else if (m[mid][0] > target)
right--;
}
if (!rowFound)
return false;
left = 0;
right = column;
int currentrow = mid;
while (left <= right)
{
mid = left + (right - left) / 2;
if (m[currentrow][mid] == target)
return true;
else if (m[currentrow][mid] < target)
left++;
else
right--;
}
return false;
}
};