2. 分治法解题
2.1 Search a 2D Matrix II
题目链接:https://leetcode.com/problems/search-a-2d-matrix-ii/description/
(1)遍历矩阵
对于这个问题,很自然地想到对矩阵进行遍历,但遍历的时间复杂度为O(m*n)。显然,这不是一个好的算法。
class Solution:
def searchMatrix(self, matrix, target):
"""
:type matrix: List[List[int]]
:type target: int
:rtype: bool
"""
if len(matrix) == 0:
return False
row, column = 0, len(matrix[0]) - 1
row_len = len(matrix) - 1
while (row <= row_len) and (column >= 0):
val = matrix[row][column]
if target == val:
return True
elif target > val:
row += 1
else:
column -= 1
return False
(2)分治思想
由于行和列都是排好序的,显然在行列的遍历过程中可以使用二分搜索使得遍历的时间复杂度降低。
如何利用分治思想呢?其实就是在行或列搜索过程中使用二分查找法!
class Solution:
def searchMatrix(self, matrix, target):
"""
:type matrix: List[List[int]]
:type target: int
:rtype: bool
"""
if len(matrix) < 1 or len(matrix[0]) < 1:
return False
for row in matrix:
if row[0] > target:
return False
elif row[len(row) - 1] < target:
continue
else:
l = 0
r = len(row) - 1
while l <= r:
mid = (l + r) // 2
if row[mid] > target:
r = mid - 1
elif row[mid] < target:
l = mid + 1
else:
return True
return False
2.2 Different Ways to Add Parentheses
题目链接:https://leetcode.com/problems/different-ways-to-add-parentheses/description/
本题若直接对表达式进行组合,考虑的情况非常多,但是可以采用分治的思想将复杂的大问题化简为若干个小问题进行求解。
下面以表达式2 * 3 - 4 * 5为例进行分析。
其实就是化简为左右两部分,然后再对每一部分进行计算,直至子问题规模为1,即左右部分只有一个元素。
class Solution:
def diffWaysToCompute(self, input):
"""
:type input: str
:rtype: List[int]
"""
if input.isdigit():
return [int(input)]
res = []
for i in range(len(input)):
if input[i] in '+-*':
res1 = self.diffWaysToCompute(input[:i])
res2 = self.diffWaysToCompute(input[i+1 :])
for j in res1:
for k in res2:
res.append(self.helper(j, k, input[i]))
res.reverse()
return res
def helper(self, j, k, op):
if op == '+':
return j + k
elif op == '-':
return j - k
else:
return j * k
显然,该方法有待改进。
2020年9月2日更新
现提供动态规划解法,至于DP核心思想后面再介绍,这里只介绍本题的DP步骤,再与分治法时间作一下对比。
- dp[i][j] 存储区间 [i,j] 内可能的结果数值,为一个列表
- 先初始化 dp[i][i]
- 建立转移方程: dp[i][j] = dp[i][k] X dp[k+1][j]( i <= k < j)(假设当前遍历的是 dp[i][k] X dp[k+1][j], 那么它们之间的运算符即为 operator[k],枚举 dp[i][k] 与 dp[k+1][j] 之间的所有结果进行运算即可。)
class Solution:
def diffWaysToCompute(self, input: str) -> List[int]:
nums, operator = self.split(input)
n = len(nums)
dp = [[list() for _ in range(n)] for _ in range(n)]
for i in range(n-1, -1, -1):
dp[i][i].append(nums[i])
for j in range(i + 1, n):
# 状态转移方程有k控制
# 对于本题举例
# dp[0][3] = dp[0][0] X dp[1][3]
# dp[0][3] = dp[0][1] X dp[2][3]
# dp[0][3] = dp[0][2] X dp[3][3]
for k in range(i, j):
for a in dp[i][k]:
for b in dp[k+1][j]:
if operator[k] == '+':
dp[i][j].append(a + b)
if operator[k] == '-':
dp[i][j].append(a - b)
if operator[k] == '*':
dp[i][j].append(a * b)
return sorted(dp[0][-1])
def split(self, input: str):
nums, operator, chars, i = [], [], list(input), 0
op = ['+', '-', '*']
while i < (length := len(input)):
if chars[i] in op:
operator.append(chars[i])
i += 1
else:
val = []
while i < length and '0' <= chars[i] <= '9':
val.append(chars[i])
i += 1
nums.append(int(''.join(val)))
return nums, operator