面试题答案
一键面试时间复杂度分析
-
不使用主定理分析:
- 对于函数
complexRecursion(int n)
,当n > 1
时,每次调用会产生三个递归调用,分别是complexRecursion(n - 1)
,complexRecursion(n - 2)
和complexRecursion(n - 3)
。 - 设
T(n)
为计算complexRecursion(n)
所需的时间。 - 当
n <= 1
时,T(n)=O(1)
,因为直接返回n
,只涉及简单的比较和返回操作,时间为常数。 - 当
n > 1
时,T(n)=T(n - 1)+T(n - 2)+T(n - 3)+O(1)
,其中O(1)
表示比较n <= 1
以及加法操作的时间。 - 由于这是一个线性递归关系,且递归深度最大为
n
,每次递归分支数为3,所以时间复杂度为指数级。可以大致估算为O(3^n)
。因为在递归树中,每层的节点数是以3的倍数增长,递归深度最大为n
。
- 对于函数
-
尝试使用主定理分析:
- 主定理的形式为
T(n)=aT(n/b)+f(n)
,其中a
是递归调用的次数,b
是每次递归中问题规模的缩小因子,f(n)
是每次递归调用之外的工作。 - 对于我们的函数
T(n)=T(n - 1)+T(n - 2)+T(n - 3)+O(1)
,不符合主定理的标准形式,因为这里不是n/b
的形式,而是n - k
(k = 1,2,3
)的形式,所以不能直接应用主定理。
- 主定理的形式为
优化方案
- 使用动态规划优化:
- 思路:通过保存已经计算过的结果,避免重复计算。
- 代码如下:
int optimizedComplexRecursion(int n) {
if (n <= 1) {
return n;
}
int dp[n + 1];
dp[0]=0;
dp[1]=1;
for (int i = 2; i <= n; i++) {
if (i == 2) {
dp[i]=dp[i - 1]+dp[i - 2]+0;
} else {
dp[i]=dp[i - 1]+dp[i - 2]+dp[i - 3];
}
}
return dp[n];
}
- 优化后时间复杂度分析:
- 这里使用了一个大小为
n + 1
的数组dp
来存储中间结果。 - 初始化
dp[0]
和dp[1]
的时间复杂度为O(1)
。 - 然后通过一个
for
循环从i = 2
到n
进行计算,每次循环内的操作时间复杂度为O(1)
,循环次数为n - 1
次。 - 所以总的时间复杂度为
O(n)
,相比优化前的O(3^n)
有了显著的降低。
- 这里使用了一个大小为
综上,原递归函数时间复杂度为O(3^n)
,优化后的动态规划函数时间复杂度为O(n)
。