I 题目:
Write an efficient algorithm that searches for a value in an m x n matrix. This matrix has the following properties:
- Integers in each row are sorted from left to right.
- The first integer of each row is greater than the last integer of the previous row.
For example,
Consider the following matrix:
[ [1, 3, 5, 7], [10, 11, 16, 20], [23, 30, 34, 50] ]
Given target = 3
, return true
.
因为排序过,思路很清晰。建立每行首元素的索引,然后行二分搜索,最后列二分搜索。复杂度是O(lg(m) + lg(n))。但是难点在于细节:
- 这里的搜索可能 False,也就是找不到元素。因此,如果 first = 1,last = 2,mid = (first + last)/2 = 1。下一次如果 first = mid = 1,又陷入了循环;
- 因为除法取整的原因,有可能 last 一直无法取到。如果 first = 1,last = 2,mid = (first + last)/2 = 1。下一次如果 first = mid = 1,还是取不到 last = 2 的值。因此需要多判断一次 last 的值;
- 关于每行首元素的索引中的搜索,不同于列搜索。即使 target > 索引中的最大值,还是有可能存在此元素。例如 target = 30 > 23,依然存在
class Solution {
public:
bool searchMatrix(vector<vector<int>>& matrix, int target) {
vector<int> rowfirst;
for(int i=0; i<matrix.size(); i++) rowfirst.push_back(matrix[i][0]);
int head = 0;
int tail = matrix.size() - 1;
while(head <= tail){
if(head + 1 == tail || head == tail){
if(target == rowfirst[head] || target == rowfirst[tail]) return true;
else break;
}
int mid = (head + tail) / 2;
if(target > rowfirst[tail]) head = tail;
else if(target == rowfirst[mid] || target == rowfirst[tail]) return true;
else if(target < rowfirst[mid]) tail = mid;
else if(target > rowfirst[mid]) head = mid;
}
if(head > tail) return false;
int first = 0;
int last = matrix[0].size() - 1;
while(first <= last){
if(first == last || first + 1 == last){
if (matrix[head][first] == target || matrix[head][last] == target) return true;
else return false;
}
int mid = (first + last) / 2;
if(target == matrix[head][mid] || target == matrix[head][last]) return true;
else if(target < matrix[head][mid]) last = mid;
else if(target > matrix[head][mid]) first = mid;
}
return false;
}
};
当然,二分搜索还有一种实现方法,就是每次必然移动一位。例如:head = mid +1,tail = mid- 1。
只是这样实现,需要在循环中单独预先判断 head,tail,mid 处的值。具体实现可参见:《Search in Rotated Sorted Array I & II》
II 题目:
Write an efficient algorithm that searches for a value in an m x n matrix. This matrix has the following properties:
- Integers in each row are sorted in ascending from left to right.
- Integers in each column are sorted in ascending from top to bottom.
For example,
Consider the following matrix:
[ [1, 4, 7, 11, 15], [2, 5, 8, 12, 19], [3, 6, 9, 16, 22], [10, 13, 14, 17, 24], [18, 21, 23, 26, 30] ]
Given target = 5
, return true
.
Given target = 20
, return false
.
解答:
我的做法比较简单,对每行的首尾遍历范围,行内二分搜索。算法复杂度为 O(m * log n):
class Solution {
public:
bool searchMatrix(vector<vector<int>>& matrix, int target) {
if (matrix.empty() || matrix[0].empty()) return false;
int m = matrix.size();
int n = matrix[0].size();
int left, right, mid;
for (int i = 0; i<m; i++) {
if (matrix[i][0] <= target && target <= matrix[i][n - 1]) {
left = 0;
right = n - 1;
while(left <= right) {
mid = (left + right) / 2;
if (matrix[i][left] == target || matrix[i][right] == target || matrix[i][mid] == target)
return true;
if (matrix[i][mid] > target) right = mid - 1;
if (matrix[i][mid] < target) left = mid + 1;
}
}
}
return false;
}
};
但其实还有一种 O(n ^ 1.58) 的复杂度:分治法。
以矩形中点为基准,将矩阵拆分成左上,左下,右上,右下四个区域。若中点值 < 目标值,则舍弃左上区域,从其余三个区域再行查找若中点值 > 目标值,则舍弃右下区域,从其余三个区域再行查找。
具体算法实现与复杂度推导(利用 “主定理 master theorem” 计算复杂度)见:传送门