递归->回溯->记忆化搜索->DP->状压DP
从最简单的一道题目谈起
Leetcode 509 斐波拉契数
仔细体会算法一步步优化的过程,文章末尾有提交记录消耗时空复杂度对比
1.递归(时间:O(2^n),空间:O(n)【实现递归需要用到系统栈】)
class Solution {
public:
int fib(int n) {
if(n==0) return 0;
else if(n==1) return 1;
else return fib(n-1)+fib(n-2);
}
};
2.回溯(时间:O(2^n),空间:O(logn)【二叉树】)
class Solution {
public:
int fib(int n) {
return dfs(n);
}
int dfs(int n){
if(n==0)return 0;
if(n==1)return 1;
int leftFib=dfs(n-1);
int rightFib=dfs(n-2);
return leftFib+rightFib;
}
};
3.记忆化搜索(时间:O(n),空间:O(n))
class Solution {
public:
int fib(int n) {
int memo[n+1];
memset(memo,-1,sizeof(memo));
return dfs(n,memo);
}
int dfs(int n, int memo[]){
if(n==0)return 0;
if(n==1)return 1;
if(memo[n]!=-1){
return memo[n];
}
int leftFib=dfs(n-1,memo);
int rightFib=dfs(n-2,memo);
memo[n]=leftFib+rightFib;
return leftFib+rightFib;
}
};
------------此处是分割线,以上都是自顶向下,下面是自底向上--------------
同时注意体会记忆化搜索和dp,其实本质是同一个东西
4.dp(时间:O(n),空间:O(n))
class Solution {
public:
int fib(int n) {
if(n<=1)return n;
int dp[n+1];
dp[0]=0,dp[1]=1;
for(int i=2;i<=n;i++){
dp[i]=dp[i-1]+dp[i-2];
}
return dp[n];
}
};
最终把状态数组压缩成两个状态,空间层面的优化
5.滚动数组+dp【滚动数组:压缩无用空间来达到优化空间复杂度的目的】
(时间:O(n),空间:O(1))
class Solution {
public:
int fib(int n) {
if(n<=1) return n;
int pre=0,cur=1;
int sum;
for(int i=2;i<=n;i++){
sum=pre+cur;
pre=cur,cur=sum;
}
return sum;
}
};
以下是所有算法在Leetcode上的提交记录
下面稍微变化一下:类比以上
LeetCode1137. 第 N 个泰波那契数
1.dp
class Solution {
public:
int tribonacci(int n) {
if(n<=1)return n;
if(n==2)return 1;
int dp[n+1];
dp[0]=0,dp[1]=1,dp[2]=1;
for(int i=3;i<=n;i++){
dp[i]=dp[i-1]+dp[i-2]+dp[i-3];
}
return dp[n];
}
};
2.滚动数组+dp
class Solution {
public:
int tribonacci(int n) {
if(n<=1)return n;
if(n==2)return 1;
int sum=0,a1=0,a2=1,a3=1;
for(int i=3;i<=n;i++){
sum=a1+a2+a3;
a1=a2,a2=a3,a3=sum;
}
return sum;
}
};