让我们以斐波那契数列为例
1,1,2,3,5,8,13,21....
first number: 1
Second number: 1
Third Number: 2
换一种说法,
Bottom(first) number: 1
Top (Eighth) number on the given sequence: 21
如果是前五个斐波那契数
Bottom(first) number :1
Top (fifth) number: 5
现在让我们以递归斐波那契数列算法为例
public int rcursive(int n) {
if ((n == 1) || (n == 2)) {
return 1;
} else {
return rcursive(n - 1) + rcursive(n - 2);
}
}
现在,如果我们使用以下命令执行此程序
rcursive(5);
如果我们仔细研究算法,为了生成第五个数字,它需要第三个和第四个数字。所以我的递归实际上是从 top(5) 开始,然后一直到底部/较低的数字。这种方法实际上是自上而下的方法。
为了避免多次进行相同的计算,我们使用动态编程技术。我们存储先前计算的值并重用它。这种技术称为记忆化。除了讨论当前问题不需要的记忆之外,动态编程还有更多内容。
自顶向下
让我们重写我们的原始算法并添加记忆技术。
public int memoized(int n, int[] memo) {
if (n <= 2) {
return 1;
} else if (memo[n] != -1) {
return memo[n];
} else {
memo[n] = memoized(n - 1, memo) + memoized(n - 2, memo);
}
return memo[n];
}
我们执行这个方法如下
int n = 5;
int[] memo = new int[n + 1];
Arrays.fill(memo, -1);
memoized(n, memo);
这个解决方案仍然是自上而下的,因为算法从最高值开始,每一步都到底部以获得我们的最高值。
自下而上
但是,问题是,我们能否从底部开始,例如从第一个斐波那契数开始,然后向上走。让我们用这种技术重写它,
public int dp(int n) {
int[] output = new int[n + 1];
output[1] = 1;
output[2] = 1;
for (int i = 3; i <= n; i++) {
output[i] = output[i - 1] + output[i - 2];
}
return output[n];
}
现在,如果我们研究这个算法,它实际上是从较低的值开始,然后到顶部。如果我需要第 5 个斐波那契数,我实际上是在计算第 1 个,然后是第 2 个,然后是第 3 个,一直到第 5 个数。这种技术实际上称为自底向上技术。
最后两个,算法完全满足动态规划要求。但一种是自上而下的,另一种是自下而上的。两种算法具有相似的空间和时间复杂度。