【Leetcode刷题】240.Search a 2D Matrix II 搜索二维矩阵 II
Link: leetcode240
Link: 力扣240
问题描述
相关Topics: Binary Search 二分查找,Divide and Conquer 分治算法
Write an efficient algorithm that searches for a target value in an m x n integer matrix. The 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.
编写一个高效的算法来搜索 m x n 矩阵 matrix 中的一个目标值 target 。该矩阵具有以下特性:
每行的元素从左到右升序排列。
每列的元素从上到下升序排列。
例子1
输入: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]], target = 5
输出:true
例子2
输入: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]], target = 20
输出:false
约束条件:
m == matrix.length
n == matrix[i].length
1 <= n, m <= 300
-109 <= matix[i][j] <= 109
All the integers in each row are sorted in ascending order.
All the integers in each column are sorted in ascending order.
-109 <= target <= 109
解法1 Brute Force 暴力法
遍历矩阵来寻找目标数字。
时间复杂度 O(n*m) ,n与m为矩阵大小
空间复杂度 O(1)
解法2 Binary Search 二分查找法
实现 binary_search 函数,传入行或列信息,进行二分查找。
时间复杂度 O(log(n!)) , 对于主循环里的第 i 次迭代来说,此时第 i 行 和 第 i 列进行二分查找的时间复杂度为 O(log(n-i)) + O(log(m-i)),当n近似于m时为最不利情况,运算量最大,近似于O(2log(n-i)) = O(log(n-i))。因此总时间复杂度为 O(log1) + O(log2) + … + Olog(n) = O(log(n!))。
空间复杂度 O(1)
class Solution:
def binary_search(self, matrix, target, start, vertical):
lo = start
hi = len(matrix[0]) - 1 if vertical else len(matrix) - 1
while hi >= lo:
mid = (lo + hi) // 2
if vertical: # searching a column
if matrix[start][mid] < target:
lo = mid + 1
elif matrix[start][mid] > target:
hi = mid - 1
else:
return True
else: # searching a row
if matrix[mid][start] < target:
lo = mid + 1
elif matrix[mid][start] > target:
hi = mid - 1
else:
return True
return False
def searchMatrix(self, matrix: List[List[int]], target: int) -> bool:
# an empty matrix obviously does not contain `target`
if not matrix:
return False
# iterate over matrix diagonals starting in bottom left.
for i in range(min(len(matrix), len(matrix[0]))):
vertical_found = self.binary_search(matrix, target, i, True)
horizontal_found = self.binary_search(matrix, target, i, False)
if vertical_found or horizontal_found:
return True
return False
解法3 Divide and Conquer 分治法
原矩阵可以拆分为四块小矩阵,只有右上↗和左下↙的两块矩阵可能存在目标数字。
时间复杂度 O(nlog(n)) , T(x) = 2*T(x/4) + x^(1/2)
这个时间复杂度的具体化简看不太懂,有待后续补充。
空间复杂度 O(log(n))
class Solution:
def searchMatrix(self, matrix: List[List[int]], target: int) -> bool:
# an empty matrix obviously does not contain `target`
if not matrix:
return False
def search_rec(left, up, right, down):
# this submatrix has no height or no width.
if left > right or up > down:
return False
# `target` is already larger than the largest element or smaller
# than the smallest element in this submatrix.
elif target < matrix[up][left] or target > matrix[down][right]:
return False
mid = left + (right-left) // 2
# Locate `row` such that matrix[row-1][mid] < target < matrix[row][mid]
row = up
while row <= down and matrix[row][mid] <= target:
if matrix[row][mid] == target:
return True
row += 1
return search_rec(left, row, mid - 1, down) or \
search_rec(mid + 1, up, right, row - 1)
return search_rec(0, 0, len(matrix[0]) - 1, len(matrix) - 1)
解法4 Search Space Reduction 减少搜索空间法
由于矩阵从行来看,从左到右是递增的,从列来看,从上到下是递增的。所以可以从左下角出发,如果currentValue > target,往上走,如果currentValue < target,往右走。
时间复杂度 O(m+n)
空间复杂度 O(1)
class Solution:
def searchMatrix(self, matrix: List[List[int]], target: int) -> bool:
# an empty matrix obviously does not contain `target` (make this check
# because we want to cache `width` for efficiency's sake)
if len(matrix) == 0 or len(matrix[0]) == 0:
return False
# cache these, as they won't change.
height = len(matrix)
width = len(matrix[0])
# start our "pointer" in the bottom-left
row = height - 1
col = 0
while col < width and row >= 0:
if matrix[row][col] > target:
row -= 1
elif matrix[row][col] < target:
col += 1
else: # found it
return True
return False