Java 动态规划实战:从递归优化到经典模型解析
回溯算法本质上是一种暴力的穷举搜索,它遍历了问题的所有可能性(状态空间树)。然而,在许多问题中,回溯搜索会产生大量的重叠子问题,导致计算资源的极度浪费。
动态规划(Dynamic Programming, DP) 并非一种具体的算法,而是一种数学优化的思维方式。它是一种通过将复杂问题分解为子问题,并存储子问题的解以避免重复计算的算法技术。它与分治法(Divide and Conquer)的区别在于:分治法的子问题通常是独立的(如归并排序),而动态规划的子问题是重叠的。
DP 的核心思想是空间换时间。通过维护一个表格(Table,通常是数组)来记录已经计算过的状态,将指数级的时间复杂度优化为多项式级(通常是线性或平方级)。
一、从递归到动态规划:思维演进
理解 DP 的最佳路径是从斐波那契数列(Fibonacci)开始。虽然这是一个简单的数学问题,但它完美展示了算法复杂度的演变。
1.1 暴力递归
斐波那契数列定义:f(n) = f(n-1) + f(n-2)。
int fib(int n) {
if (n <= 1) return n;
return fib(n - 1) + fib(n - 2);
}
分析:
该算法的执行流程是一棵二叉树。计算 f(20) 需要计算 f(19) 和 f(18),而计算 f(19) 又需要计算 f(18) 和 f(17)。
- 重叠计算:
f(18)被重复计算了多次。随着n增大,重复计算呈指数级爆炸。 - 复杂度:
O(2^n)。当n=50时,计算量将达到千万亿级,程序会超时。
1.2 记忆化搜索
为了解决重复计算,我们可以引入一个'备忘录'(Memo),即一个数组或哈希表。每次计算 f(n) 前,先查表;计算完后,将结果存入表。
int[] memo;
public int fib(int n) {
memo = new int[n + 1];
return helper(n);
}
int helper(int n) {
if (n <= 1) n;
(memo[n] != ) memo[n];
memo[n] = helper(n - ) + helper(n - );
memo[n];
}

