LeetCode 120:自顶向下动态规划求三角形最小路径和
2023-11-01 06:47:46
从三角形顶点到底部的最小路径和
简介
在三角形中穿行,从顶点到底部,每一步只能移动到相邻的结点,如何在每一步都做出正确的选择,以找到从顶点到底部最省力的路径?这个问题看似简单,却蕴含着数学和计算机科学中的奥秘。
动态规划解法
动态规划是一种自底向上的问题求解方法,我们将问题分解成较小的子问题,逐步解决并存储子问题的解,避免重复计算。对于三角形最小路径和问题,我们定义dp[i][j]
表示从顶点到第i
行第j
列元素的最小路径和。从底向上逐行计算,利用子问题的解来推导出当前行的解。
代码示例:
def min_path_triangle(triangle):
"""
返回三角形从顶点到底部的最小路径和。
"""
# 初始化动态规划表
n = len(triangle)
dp = [[0] * i for i in range(1, n + 1)]
# 从底向上计算动态规划表
for i in range(n - 1, -1, -1):
for j in range(i + 1):
# 计算当前元素的最小路径和
dp[i][j] = min(dp[i + 1][j], dp[i + 1][j + 1]) + triangle[i][j]
# 返回顶点的最小路径和
return dp[0][0]
深度优先搜索解法
深度优先搜索是一种递归算法,它沿着一条路径深度探索,直到找到解或遍历完所有可能性。对于三角形最小路径和问题,我们可以使用深度优先搜索从顶点开始探索,沿着每条可能的路径进行递归,直到到达底部,并返回最小的路径和。
代码示例:
def min_path_dfs(triangle):
"""
使用深度优先搜索查找三角形从顶点到底部的最小路径和。
"""
def dfs(i, j):
# 达到底部,返回当前元素的路径和
if i == len(triangle) - 1:
return triangle[i][j]
# 沿着左子树和右子树递归探索
left = dfs(i + 1, j)
right = dfs(i + 1, j + 1)
# 返回较小的路径和
return min(left, right) + triangle[i][j]
# 从顶点开始深度优先搜索
return dfs(0, 0)
递归解法
递归是一种将问题分解成相同或相似的子问题的算法。对于三角形最小路径和问题,我们可以使用递归从顶点开始,沿着每条可能的路径进行递归,直到到达底部,并返回最小的路径和。
代码示例:
def min_path_recursive(triangle):
"""
使用递归查找三角形从顶点到底部的最小路径和。
"""
def dfs(i, j):
# 达到底部,返回当前元素的路径和
if i == len(triangle) - 1:
return triangle[i][j]
# 沿着左子树和右子树递归探索
left = dfs(i + 1, j)
right = dfs(i + 1, j + 1)
# 返回较小的路径和
return min(left, right) + triangle[i][j]
# 从顶点开始递归
return dfs(0, 0)
常见问题解答
-
为什么使用动态规划来解决这个问题?
动态规划是一种高效的算法,它可以避免重复计算,从而节省时间和空间复杂度。 -
深度优先搜索和递归算法之间有什么区别?
深度优先搜索沿着一条路径深度探索,而递归将问题分解成相同或相似的子问题。 -
为什么在三角形的底部行初始化动态规划表?
因为底部的最小路径和就是每个元素本身,这是动态规划算法的基础。 -
为什么在深度优先搜索中计算左子树和右子树的最小路径和?
因为从当前元素只能移动到下一行中的相邻结点,因此我们需要考虑这两个路径。 -
为什么递归算法的时间复杂度为指数级?
因为递归算法会沿着每条可能的路径递归探索,因此随着三角形高度的增加,时间复杂度呈指数增长。