11 动态规划与经典数据结构的结合

在上一篇文章中,我们探讨了动态规划的基本概念,包括其核心思想以及基本的状态转移方程。接下来,我们将进一步深入动态规划与经典数据结构的结合,探讨如何利用合适的数据结构来优化动态规划的实现。这一部分将极大地提高我们解决问题的效率和灵活性。

一、动态规划结合数据结构的必要性

动态规划是一种解决复杂问题的方法,它通过将问题分解为较小的子问题来工作。如果我们能够有效地存储和访问这些子问题的解决方案,便能避免在解决问题时重复计算,提高效率。这就是为什么选择合适的数据结构至关重要。

1. 使用数组

最基本的动态规划实现通常是简单的数组。例如,在解决斐波那契数列时,我们可以用一个一维数组来存储中间结果,如下所示:

1
2
3
4
5
6
def fibonacci(n):
dp = [0] * (n + 1)
dp[1] = 1
for i in range(2, n + 1):
dp[i] = dp[i - 1] + dp[i - 2]
return dp[n]

在这个例子中,我们使用了一维数组dp来存储每个状态的结果,避免了重复计算。

2. 使用哈希表

在一些情况下,子问题的状态空间可能比较大或者稀疏,这时我们可以使用哈希表来存储解决方案。例如,在解决不同路径数量的问题时,我们可以选择使用哈希表来存储每个状态的值:

1
2
3
4
5
6
7
8
9
10
11
12
def unique_paths(m, n):
memo = {}

def dp(x, y):
if (x, y) in memo:
return memo[(x, y)]
if x == 1 or y == 1:
return 1
memo[(x, y)] = dp(x - 1, y) + dp(x, y - 1)
return memo[(x, y)]

return dp(m, n)

在这里,我们使用memo哈希表存储每个状态的结果,以避免多次计算相同的状态。

二、深入经典数据结构的结合

1. 栈(Stack)

栈是一种典型的后进先出(LIFO)数据结构。在动态规划中,我们可以使用栈来维护状态,例如在实现深度优先搜索(DFS)时,可以利用栈来保存路径信息。我们可以结合动态规划来解决某些路径问题,例如“最大矩形面积”问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
def maximalRectangle(matrix):
if not matrix:
return 0
heights = [0] * len(matrix[0])
max_area = 0

for row in matrix:
for i in range(len(row)):
heights[i] = heights[i] + 1 if row[i] == '1' else 0
max_area = max(max_area, largestRectangleArea(heights))

return max_area

def largestRectangleArea(heights):
stack = []
max_area = 0
heights.append(0)

for i in range(len(heights)):
while stack and heights[stack[-1]] > heights[i]:
h = heights[stack.pop()]
w = i if not stack else i - stack[-1] - 1
max_area = max(max_area, h * w)
stack.append(i)

return max_area

通过使用栈,我们可以高效地计算每一行的最大矩形面积。

2. 队列(Queue)

队列是一种先进先出(FIFO)数据结构,适用于解决某些特殊的动态规划问题,如“01背包问题”的多维动态规划。

1
2
3
4
5
6
7
8
9
10
11
from collections import deque

def knapsack(weights, values, capacity):
n = len(weights)
dp = [0] * (capacity + 1)

for i in range(n):
for w in range(capacity, weights[i] - 1, -1):
dp[w] = max(dp[w], dp[w - weights[i]] + values[i])

return dp[capacity]

在这个例子中,我们没有直接利用队列,但可以考虑在某些优化版本中使用双端队列(deque)来维护一些状态,从而优化时间复杂度。

3. 树(Tree)

树状结构在动态规划中有着广泛的应用,例如在树的深度优先搜索中,我们可以利用递归和动态规划结合来计算树的某些特性,比如树的最大路径和。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right

def maxPathSum(root):
def dfs(node):
if not node:
return 0
left = max(dfs(node.left), 0)
right = max(dfs(node.right), 0)
max_sum[0] = max(max_sum[0], left + right + node.val)
return max(left, right) + node.val

max_sum = [-float('inf')]
dfs(root)
return max_sum[0]

在这个例子中,我们借助深度优先搜索来动态计算树的路径和,同时使用动态规划的思想来优化我们的结果。

三、总结

通过结合经典数据结构与动态规划,我们能够更高效地解决许多复杂的问题。选择合适的数据结构不仅可以提高代码的运行效率,还能使问题建模更加简洁明了。在后续的文章中,我们将深入探讨动态规划的实例分析,重点聚焦于最优子结构的概念及其应用。

在这个系列中,你不仅会获得动态规划理论的指导,也会对如何有效利用数据结构有更深的理解和实践。希望你们能在深入学习中收获满满!

11 动态规划与经典数据结构的结合

https://zglg.work/datastructure-one/11/

作者

IT教程网(郭震)

发布于

2024-08-11

更新于

2024-08-12

许可协议

分享转发

学习下节

交流

更多教程加公众号

更多教程加公众号

加入星球获取PDF

加入星球获取PDF

打卡评论