74. 搜索二维矩阵
- 编写一个高效的算法来判断 m x n 矩阵中,是否存在一个目标值。该矩阵具有如下特性:
每行中的整数从左到右按升序排列。
每行的第一个整数大于前一行的最后一个整数。
示例:
示例 1:
输入:
matrix = [
[1, 3, 5, 7],
[10, 11, 16, 20],
[23, 30, 34, 50]
]
target = 3
输出: true
示例 2:
输入:
matrix = [
[1, 3, 5, 7],
[10, 11, 16, 20],
[23, 30, 34, 50]
]
target = 13
输出: false
思路1:
取每行数组的第一个元素与target判断,若target大于当前行第一个元素,且小于下一行第一个元素,则在此行进行匹配。
代码实现1:
class Solution:
def searchMatrix(self, matrix: List[List[int]], target: int) -> bool:
# 特殊情况判断:1、矩阵为空 2、矩阵第一个元素为空 3、目标值为空 都返回False
if not (matrix and matrix[0] and len(str(target))):
return False
for i in range(len(matrix)):
# 定义一个标志,当目标值匹配到了某一行时,设为1,便于当目标值在此行未匹配到值时,函数直接结束
flag = 0
# 判断矩阵是否只有一行,防止越界
if i + 1 < len(matrix):
# 如果目标值匹配到某一行,将flag设为1,并在此行寻找相等的元素
if matrix[i][0] <= target < matrix[i + 1][0]:
flag = 1
for j in matrix[i]:
if j == target:
return True
if flag:
return False
else:
for j in matrix[i]:
flag = 1
if j == target:
return True
if flag:
return False
思路2:二分查找
注意到输入的 m x n 矩阵可以视为长度为 m x n 的有序数组。
数组的序号可以由下式方便地转化为原矩阵中的行和列
row = idx // n , col = idx % n。
标准二分查找算法 :
初始化左右序号
left = 0 和 right = m x n - 1。
While left < right :
选取虚数组最中间的序号作为中间序号: mid = (left + right) // 2。
该序号对应于原矩阵中的 row = mid // n行 col = mid % n 列, 由此可以拿到中间元素 elem。该元素将数组分为两部分。
比较 elem 与 target 以确定在哪一部分进行进一步查找。
代码实现2:
class Solution:
def searchMatrix(self, matrix: List[List[int]], target: int) -> bool:
# 特殊情况判断
if len(matrix) == 0:
return False
m, n = len(matrix), len(matrix[0])
left, right = 0, m * n - 1
while left <= right:
mid = (left + right) // 2
elem = matrix[mid // n][mid % n]
if target == elem:
return True
else:
if target <= elem:
right = mid - 1
else:
left = mid + 1
return False
240. 搜索二维矩阵II
- 编写一个高效的算法来搜索 m x n 矩阵 matrix 中的一个目标值 target。该矩阵具有以下特性:
每行的元素从左到右升序排列。
每列的元素从上到下升序排列。
示例:
现有矩阵 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。
给定 target = 20,返回 false。
思路1:二分法
首先,我们确保矩阵不为空。那么,如果我们迭代矩阵对角线,从当前元素对列和行搜索,我们可以保持从当前 (row,col)(row,col) 对开始的行和列为已排序。 因此,我们总是可以二分搜索这些行和列切片。我们以如下逻辑的方式进行 : 在对角线上迭代,二分搜索行和列,直到对角线的迭代元素用完为止(意味着我们可以返回 false )或者找到目标(意味着我们可以返回 true )。binary search 函数的工作原理和普通的二分搜索一样,但需要同时搜索二维数组的行和列。
代码实现1:
def binary_search(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(matrix, target):
# 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 = binary_search(matrix, target, i, True)
horizontal_found = binary_search(matrix, target, i, False)
if vertical_found or horizontal_found:
return True
return False
思路2:双指针(推荐)
矩阵有四个角,从角落出发:
- 选左上角,往右走和往下走都增大,不能选
- 选右下角,往上走和往左走都减小,不能选
- 选左下角,往右走增大,往上走减小,可选
- 选右上角,往下走增大,往左走减小,可选
如选左下角:
- 首先,我们初始化一个指向矩阵左下角的 [len(matrix) - 1][0] 指针。判断当前指针与目标值的大小关系,直到找到目标并返回True或者指针指向矩阵维度之外返回False。
- 根据行是从左到右排序的,列是从上到下排序的,我们执行以下操作:如果当前指向的值大于目标值,则可以 “向上” 移动一行。 否则,如果当前指向的值小于目标值,则可以“向右”移动一列。
代码实现2:
class Solution:
def searchMatrix(self, matrix, target):
if len(matrix) == 0:
return False
width = len(matrix[0])
height = len(matrix)
row = height - 1
col = 0
while row >= 0 and col < width:
if target > matrix[row][col]:
col += 1
elif target < matrix[row][col]:
row -= 1
else:
return True
return False
思路3:分治算法
我们可以将已排序的二维矩阵划分为四个子矩阵,其中两个可能包含目标,其中两个肯定不包含。
代码实现3:
class Solution:
def searchMatrix(matrix, target):
if not matrix:
return False
def search_rec(left, up, right, down):
# 指针越界或值越界
if left > right or up > down:
return False
elif target < matrix[up][left] or target > matrix[down][right]:
return False
mid = (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)