各位题友大家好! 今天是 @负雪明烛 坚持日更的第 65 天。今天力扣上的每日一题是「74. 搜索二维矩阵」。

解题思路

今天题目重点:每行数字都是升序的;每行的所有数字都比它上面的那一行大。

下面给了多种方法,效率越来越快。

方法一:遍历

该方法就是遍历查找每个位置,看 target 是否出现。这个方法也能通过本题。

  1. class Solution(object):
  2. def searchMatrix(self, matrix, target):
  3. M, N = len(matrix), len(matrix[0])
  4. for i in range(M):
  5. for j in range(N):
  6. if matrix[i][j] == target:
  7. return True
  8. return False
  1. class Solution(object):
  2. def searchMatrix(self, matrix, target):
  3. return any(target in row for row in matrix)
  1. class Solution {
  2. public:
  3. bool searchMatrix(vector<vector<int>>& matrix, int target) {
  4. if (matrix.size() == 0 || matrix[0].size() == 0) return false;
  5. const int M = matrix.size(), N = matrix[0].size();
  6. int i = 0;
  7. while (i < M * N) {
  8. if (matrix[i / N][i % N] == target)
  9. return true;
  10. ++i;
  11. }
  12. return false;
  13. }
  14. };
  • 时间复杂度: $O(M * N)$
  • 空间复杂度:$O(1)$

方法二:从左下角或者右上角开始查找

今天这个题目是 240. 搜索二维矩阵 II 的一个特例。建议也把 240 题做一下。

这个方法是利用了矩阵的性质,如果我们从右上角开始遍历:

  • 如果要搜索的 target 比当前元素大,那么让行增加;
    - 如果要搜索的 target 比当前元素小,那么让列增加;
  1. class Solution(object):
  2. def searchMatrix(self, matrix, target):
  3. if not matrix or not matrix[0]:
  4. return False
  5. rows = len(matrix)
  6. cols = len(matrix[0])
  7. row, col = 0, cols - 1
  8. while True:
  9. if row < rows and col >= 0:
  10. if matrix[row][col] == target:
  11. return True
  12. elif matrix[row][col] < target:
  13. row += 1
  14. else:
  15. col -= 1
  16. else:
  17. return False
  • 时间复杂度:$O(M + N)$
  • 空间复杂度:$O(1)$

方法三:先寻找到所在行

该方法利用了题目给出的矩阵的性质:每行元素都是单调递增的,并且下一行的元素会比本行更大。所以:

如果 target 大于这一行的末尾元素,那么target 一定不在这一行中,只能出现在矩阵的下面的行中。

那么,假如 target < matrix[i][N - 1] 时,说明 target 可能在本行中出现,而且由于下面各行的元素都大于 matrix[i][N - 1] ,所以,不可能在下面的行中出现。此时,可以在本行中使用顺序查找,或者二分查找。

  1. class Solution(object):
  2. def searchMatrix(self, matrix, target):
  3. M, N = len(matrix), len(matrix[0])
  4. for i in range(M):
  5. if target > matrix[i][N - 1]:
  6. continue
  7. if target in matrix[i]:
  8. return True
  9. return False
  1. class Solution {
  2. public:
  3. bool searchMatrix(vector<vector<int>>& matrix, int target) {
  4. if (matrix.size() == 0 || matrix[0].size() == 0) return false;
  5. const int M = matrix.size(), N = matrix[0].size();
  6. for (int i = 0; i < M; ++i) {
  7. if (target > matrix[i][N - 1])
  8. continue;
  9. auto it = find(matrix[i].begin(), matrix[i].end(), target);
  10. return it != matrix[i].end();
  11. }
  12. return false;
  13. }
  14. };
  1. class Solution(object):
  2. def searchMatrix(self, matrix, target):
  3. M, N = len(matrix), len(matrix[0])
  4. for i in range(M):
  5. if target > matrix[i][N - 1]:
  6. continue
  7. index = bisect.bisect_left(matrix[i], target)
  8. if matrix[i][index] == target:
  9. return True
  10. return False
  • 时间复杂度: 在行中遍历查找的时间复杂度是: $O(M + N)$;在行中进行二分查找的时间复杂度是 $O(M + log(N))$
  • 空间复杂度:$O(1)$


方法四:两次二分查找

这个方法可以说是方法三的改进。在方法三种,我们是先遍历找到 target 在哪一行,然后在该行遍历或者二分查找的 target 。其实也可以先用二分找到 target 所在的行,然后在该行二分找到 target。

具体做法是,先找到 matrix[i][0] 小于 target 并且 matrix[i + 1][0] > target 的第 i 行,然后在该行内进行二分找到 target

代码如下:

  1. class Solution(object):
  2. def searchMatrix(self, matrix, target):
  3. M, N = len(matrix), len(matrix[0])
  4. col0 = [row[0] for row in matrix]
  5. target_row = bisect.bisect_right(col0, target) - 1
  6. if target_row < 0:
  7. return False
  8. target_col = bisect.bisect_left(matrix[target_row], target)
  9. if target_col >= N:
  10. return False
  11. if matrix[target_row][target_col] == target:
  12. return True
  13. return False
  • 时间复杂度: $O(log(M) + log(N))$
  • 空间复杂度:$O(1)$

方法五:全局二分

这个方法,是我们在二维矩阵上进行二分查找,这其实相当于把二维矩阵当做一维来做,要求每一行的最后一个元素小于下一行的第一个元素。

根据 mid 求出在二维矩阵中的具体位置,然后判断 left 和 right 的移动方式。整体做法和一维数组的二分没有区别。

  1. class Solution(object):
  2. def searchMatrix(self, matrix, target):
  3. M, N = len(matrix), len(matrix[0])
  4. left, right = 0, M * N - 1
  5. while left <= right:
  6. mid = left + (right - left) // 2
  7. cur = matrix[mid // N][mid % N]
  8. if cur == target:
  9. return True
  10. elif cur < target:
  11. left = mid + 1
  12. else:
  13. right = mid - 1
  14. return False
  • 时间复杂度: $O(log(M * N))$
  • 空间复杂度:$O(1)$

    方法六:reshape成一维数组

如果用python刷题,在leetcode中是支持使用 numpy 的,可以把 matrix成一维有序的数组,然后按照一维数组去操作查找。

  1. import numpy as np
  2. class Solution(object):
  3. def searchMatrix(self, matrix, target):
  4. matrix = np.reshape(matrix, [1, -1])
  5. return target in matrix
  1. import numpy as np
  2. class Solution(object):
  3. def searchMatrix(self, matrix, target):
  4. matrix = np.reshape(matrix, [1, -1]).tolist()[0]
  5. index = bisect.bisect_left(matrix, target)
  6. if index < 0 or index >= len(matrix):
  7. return False
  8. return matrix[index] == target
  • 时间复杂度: $O(MN + log(MN))$
  • 空间复杂度:$O(M*N)$

刷题心得

参考资料:
负雪明烛
力扣官方题解


OK,以上就是 @负雪明烛 写的今天题解的全部内容了,如果你觉得有帮助的话,求赞、求关注、求收藏。如果有疑问的话,请在下面评论,我会及时解答。

关注我,你将不会错过我的精彩动画题解、面试题分享、组队刷题活动,进入主页 @负雪明烛 右侧有刷题组织,从此刷题不再孤单。

祝大家牛年大吉!AC 多多,Offer 多多!我们明天再见!