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; } };