做了几道动态规划的题目,对部分代码进行了空间优化。
一维dp
70.爬楼梯
class Solution {
public:
int climbStairs(int n) {
vector<int> d(n+1);//空间复杂度 O(n)
//边界条件
d[0] = 1;//跳到第一个台阶,定义d[0]=0没有意义
d[1] = 2;
for (int i = 2; i < n; i++) {
d[i] = d[i - 1] + d[i - 2];
}
return d[n-1];
}
};
空间优化
class Solution {
public:
int climbStairs(int n) {
vector<int> d(2);//空间复杂度 O(1)
// 边界
d[0]=1;
d[1]=2;
for(int i=2;i<n;i++){
d[i%2]=d[1]+d[0];
}
return d[(n-1)%2];
}
};
264.丑数||
class Solution {
public:
int nthUglyNumber(int n) {
// 丑数=2^a+3^b+5^c
// p2,p3,p5是指针索引,表示当前位置(含)之前的能被pi整除的丑数个数
vector<int > dp(n);
dp[0]=1;
int p2=0,p3=0,p5=0;
for(int i=1;i<n;i++){
int n2=dp[p2]*2,n3=dp[p3]*3,n5=dp[p5]*5;
dp[i]=min({n2,n3,n5});
if(dp[i]==n2) p2++;
if(dp[i]==n3) p3++;
if(dp[i]==n5) p5++;
}
return dp[n-1];
}
};
746:使用最小花费爬楼梯
从后往前:
class Solution {
public:
int minCostClimbingStairs(vector<int>& cost) {
int n = cost.size();
vector<int> d(n + 1);
d[n] = 0;//顶部不用再向上跳,花费为0
d[n - 1] = cost[n - 1];
for (int i = n - 2; i >= 0; i--) {
d[i] = cost[i] + min(d[i + 1], d[i + 2]);
}
return min(d[0], d[1]);
}
};
二维dp
72.编辑距离
空间复杂度: O ( n 2 ) O(n^2) O(n2)
class Solution {
public:
int minDistance(string word1, string word2) {
int n = word1.length(), m = word2.length();
if (n * m == 0) {
return n + m;
}
vector<vector<int>> dp(n + 1, vector<int>(m + 1, 0));
for (int i = 0; i < n + 1; i++) {
for (int j = 0; j < m + 1; j++) {
if (i == 0 || j == 0) { // 初始化
dp[i][j] = i ^ j;
} else if (word1[i - 1] == word2[j - 1]) {
dp[i][j] = dp[i - 1][j - 1]; // 相等不用编辑
} else {
dp[i][j] = min({dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]}) + 1;
}
}
}
return dp[n][m];
}
};
空间复杂度:
O
(
2
n
)
O(2n)
O(2n),即
O
(
n
)
O(n)
O(n),滚动数组,dp
是一个两行的二维数组。
class Solution {
public:
int minDistance(string word1, string word2) {
int n = word1.length(), m = word2.length();
if (n * m == 0) {
return n + m;
}
vector<vector<int>> dp(2, vector<int>(m + 1, 0));
int i,j;
for (i = 0; i < n + 1; i++) {
for (j = 0; j < m + 1; j++) {
if (i == 0 || j == 0) { // 两个for循环改成一个if判断
dp[i%2][j] = i ^ j;
} else if (word1[i - 1] == word2[j - 1]) {
dp[i%2][j] = dp[(i+1)%2][j - 1]; // 相等不用编辑
} else {
dp[i%2][j] = min({dp[(i+1)%2][j], dp[i%2][j - 1], dp[(i+1)%2][j - 1]}) + 1;
}
}
}
return dp[(i+1)%2][m];//此时i==n+1
}
};
和上面的差不多,用两个一维vector
。
class Solution {
public:
int minDistance(string word1, string word2) {
int n = word1.length(), m = word2.length();
if (n * m == 0) {
return n + m;
}
vector<int> predp(m + 1,0);
vector<int> curdp(m + 1,0);
for (int i = 0; i < n + 1; i++) {
for (int j = 0; j < m + 1; j++) {
if (i == 0 || j == 0) { // 两个for循环改成一个if判断
curdp[j] = i ^ j;
} else if (word1[i - 1] == word2[j - 1]) {
curdp[j] = predp[j - 1]; // 相等不用编辑
} else {
curdp[j] = min({ curdp[j - 1], predp[j-1], predp[j]}) + 1;
}
}
swap(curdp,predp);
}
return predp[m];//结果被放到predp中了
}
};
空间复杂度
O
(
n
)
O(n)
O(n),用一个一维vector
。用变量leftUp
记录左上角的元素。
class Solution {
public:
int minDistance(string word1, string word2) {
int n = word1.length(), m = word2.length();
if (n * m == 0) {
return n + m;
}
vector<int> dp(m + 1, 0);
int leftUp; // 记录更新时的左上角元素,即原来的dp[i-1][j-1]
for (int i = 0; i < n + 1; i++) {
for (int j = 0; j < m + 1; j++) {
int temp = dp[j]; // 记录更新前的dp[j]
if (i == 0 || j == 0) {
dp[j] = i ^ j;
} else if (word1[i - 1] == word2[j - 1]) {
dp[j] = leftUp; // 相等不用编辑
} else {
dp[j] = min({dp[j], dp[j - 1], leftUp}) + 1;
}
leftUp = temp;
}
}
return dp[m];
}
};
1049. 最后一块石头的重量 ||
0-1背包问题,当weight数组与value数组一样时,问题转化为选择子集的问题,也就是找出和最接近totalWeight/2
的一个子集。
class Solution {
public:
int lastStoneWeightII(vector<int>& stones) {
// 求和,0表示累加的初始值,totalWeight与0的类型保持一致
int totalWeight = accumulate(stones.begin(), stones.end(), 0);
int halfWeight = totalWeight / 2;
vector<int> dp(halfWeight + 1, 0);
// 动态规划填充dp数组
for (int stone : stones) {
for (int j = halfWeight; j >= stone; --j) {
dp[j] = max(dp[j], dp[j - stone] + stone);
}
}
//( totalWeight - dp[halfWeight]) - dp[halfWeight]表示两组石头的差值
return totalWeight - 2 * dp[halfWeight];
}
};