leetcode 240 二维矩阵查值 题解

题目

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.

Example:

Consider the following matrix:

1
2
3
4
5
6
7
[
[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.

解法一

拿到题目一看是查询类的,先暴力解了再说,解法就是两层循环遍历找到值。实现如下:

1
2
3
4
5
6
7
8
9
10
11
bool searchMatrix(vector<vector<int>>& matrix, int target) {
if(matrix.empty() || matrix[0].empty()) return false;
for(int i = 0; i < matrix.size(); i++){
for(int j = 0; j < matrix[i].size(); j++){
if(matrix[i][j] == target){
return true;
}
}
}
return false;
}

容易看出,这样做的复杂度达到了O(m*n)(m、n对应矩阵的行和列),肯定是不能够让人满意的。

解法二

接下来有哪些地方可以改进的呢?注意这个二维矩阵,暴力求解的时候对每一行是直接顺序扫描,那么这里可以改成对一行进行二分查找,这样一来复杂度可以减少到O(m*logn),实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
bool bisearch(vector<int>& line, int head, int tail, int target){
int mid = line[(head + tail) / 2];
if(head == tail){
return mid == target;
}
if(mid == target){
return true;
}
if(mid < target){
return bisearch(line, (head + tail) / 2 + 1, tail, target);
}
else{
return bisearch(line, head, (head + tail) / 2, target);
}
}
bool searchMatrix(vector<vector<int>>& matrix, int target) {
if(matrix.empty() || matrix[0].empty()) return false;
for(int i = 0; i < matrix.size(); i++){
if(matrix[i].size() == 1 && matrix[i][0] == target){
return true;
}
else{
if(target > matrix[i][matrix[i].size() - 1]) continue;
if(target < matrix[i][0]) return false;
if(bisearch(matrix[i], 0, matrix[i].size() - 1, target)){
return true;
}
}
}
return false;
}

解法三

按照上一个解法提交后我发现成绩还是不尽人意(运行时间在所有成功提交中在中间的位置),那么还能怎么改进呢?思考了许久后,我发现是自己一开始就陷入思维定势了,因为这道题类型属于divide and conquer,所以就一直想着二分,其实并不一定要这么做。

重点是要分析透彻这个矩阵的特点,它的每一行、列的元素都是按从小到大的顺序排列好的,所以每个元素的左边元素都比它小,下面元素都比它大,那么只需要从第一行最右元素开始比较,若比target小就向下移动一位(这一行剩下的不需要查找了),若比target大就向左移动一位,这样若矩阵中存在target那么肯定能够找到。其实从最后一行第一个元素开始也可以,原理是一样的。

实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
bool searchMatrix(vector<vector<int>>& matrix, int target) {
if(matrix.empty() || matrix[0].empty()) return false;
int i = 0, j = matrix[0].size() - 1;
while(j >= 0 && i < matrix.size()){
if(matrix[i][j] == target){
return true;
}
else if(matrix[i][j] > target){
j--;
}
else{
i++;
}
}
return false;
}

值得一提的是,这样完成的复杂度最差情况下为O(m+n)。