我在InterviewBit上遇到了这个问题:
int memo[101][101];
int findMinPath(vector<vector<int> > V, int r, int c) {
int R = V.size();
int C = V[0].size();
if (r >= R || c >= C) return 100000000; // Infinity
if (r == R - 1 && c == C - 1) return 0;
if (memo[r][c] != -1) return memo[r][c];
memo[r][c] = V[r][c] + min(findMinPath(V, r + 1, c), findMinPath(V, r, c + 1));
return memo[r][c];
}
Callsite :
memset(memo, -1, sizeof(memo));
findMinPath(V, 0, 0);
假设R = V.size()
和C = V[0].size()
以及V
有积极因素
代码是否会创建一个二元树类型的函数调用,它接受O(2 (m + n)),因为每个函数调用都会调用另外两个函数?
答案 0 :(得分:3)
此算法使用称为dynamic programming的技术。其实质是在查找表中记忆中间结果,并将整体解决方案作为部分解决方案的组合。
在这种特殊情况下,您可以看到findMinPath
的每个调用都是一个叶子调用(不是递归),具有恒定的复杂性,或者使memo
中的至少一个条目变为非负的。很容易看出,如果memo
中的所有条目都是非负数,则该函数将永远不会递归。由于memo
中只有 R × C 元素,因此这是整体复杂性的上限。
那就是说,实施相当笨拙。为什么要为memo
使用全局变量并依赖于调用者将其初始化为-1
?此外,使用memset
编写整数数组仅适用于所有字节相同的整数,这会对整数布局产生不可移植的假设。最后,如果 R 或 C 超过魔术值101,则存在缓冲区溢出漏洞。因此,如果您想要迂腐,实际复杂性是不变的。< / p>
答案 1 :(得分:0)
请注意,对于给定的(r,c)
以下代码将不会执行一次以上:
memo [r] [c] = V [r] [c] + min(findMinPath(V,r + 1,c),findMinPath(V,r,c + 1));
一旦设置了备忘[r] [c],功能将返回
如果(memo [r] [c]!= -1)返回备忘录[r] [c];
因此,每个函数最多只能调用1个其他函数。 换句话说,每个函数最终最多执行O(1)次(请注意,您可以在调用站点上转移有关memo [r] [c]!= -1的检查部分)。
O(R * C)可能有(r,c)个组合 因此,函数的时间复杂度为:O(R * C)
答案 2 :(得分:0)
此代码最坏的情况是时间复杂度为O(R*C)
,因为我们使用自顶向下方法通过将值存储在memo [r] [c]中来实现动态编程,因此仅进行递归调用一次,第二次它将执行以下行
if(memo[r][c] != -1) return memo[r][c]
,其复杂度为O(1)。
可能的总组合为R*C
,因此时间复杂度为O(R*C)