简单练习
1.斐波拉契数列
斐波那契数,通常用 F(n) 表示,形成的序列称为 斐波那契数列 。该数列由 0 和 1 开始,后面的每一项数字都是前面两项数字的和。也就是: F(0) = 0,F(1) = 1 F(n) = F(n - 1) + F(n - 2),其中 n > 1 给你n ,请计算 F(n) 。
方法一:自上而下,递归
//递归,自上而下求解
//递归,自上而下求解
class Solution {
public:
int fib(int n) {
if (n == 0) return 0;
if (n == 1) return 1;
int rValue = fib(n - 1) + fib(n - 2);
return rValue;
}
};
方法二:自下而上,动态规划
class Solution {
public:
int fib(int n) {
if (n == 0) return 0;
if (n == 1) return 1;
int *record = new int[n + 1];//0,1,2,3,4,...,n
record[0] = 0;
record[1] = 1;
for (int i = 2; i <= n; i++) {
record[i] = record[i - 1] + record[i - 2];
}
return record[n];
}
};
2.爬楼梯
假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
分析:
设f(n)表示爬n阶楼梯的方法总数,则:
f(1)=1
f(2)=2
f(3)=f(1)+f(2)
f(n)=f(n-2)+f(n-1)
最后一步有两种状态:跨两阶或者一阶,分别对应f(n-2)和f(n-1)中爬法
代码于斐波拉契数列类似
class Solution {
public:
int climbStairs(int n) {
if (n == 0) return 0;
if (n == 1) return 1;
if (n == 2) return 2;
int *record = new int[n + 1];//0,1,2,3,4,...,n
record[0] = 0;
record[1] = 1;
record[2] = 2;
for (int i = 3; i <= n; i++) {
record[i] = record[i - 1] + record[i - 2];
}
return record[n];
}
};
3.使用最小花费爬楼梯
给你一个整数数组 cost ,其中 cost[i] 是从楼梯第 i 个台阶向上爬需要支付的费用。一旦你支付此费用,即可选择向上爬一个或者两个台阶。
你可以选择从下标为 0 或下标为 1 的台阶开始爬楼梯。
请你计算并返回达到楼梯顶部的最低花费。
分析:在问题2的基础上加入了花费,典型的动态规划问题,符合前面的四个特性
最后一步跨两阶或者一阶,对应两种状态
令f(n)表示爬上n阶楼梯的最小花费
f(n)=min{f(n-1)+cost[n-1], f(n-2)+cost[n-2]}
代码:
class Solution {
public:
int minCostClimbingStairs(vector<int>& cost) {
int n = cost.size();
if (n == 0) return 0;
if (n == 1) return 0;
if (n == 2) return min(cost[0], cost[1]);
int *record = new int[n + 1];//0,1,2,3,4,...,n
record[0] = 0;
record[1] = 0;
for (int i = 2; i <= n; i++) {
record[i] = min(record[i - 1] + cost[i - 1], record[i - 2] + cost[i - 2]);
}
return record[n];
}
};
4.不同路径
一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。
问总共有多少条不同的路径?
![](https://i-blog.csdnimg.cn/blog_migrate/f4bf89a565826660b26f52c9f0159fd1.png)
分析:这题与前两道爬楼梯很相似,到达终点前只有两个状态
设机器人到达(m,n)有f(m,n)种不同的路径
则f(m,n)=f(m-1,n)+f(m,n-1)
注意边界处理:
左边界和上边界的每个格子,分别等于其上面、左边格子的路径数
class Solution {
public:
int uniquePaths(int m, int n) {
vector<vector<int>> record(m, vector<int>(n));
for (int i = 0; i <= m - 1; i++) {
for (int j = 0; j <= n - 1; j++) {
if (i == 0 && j == 0) record[i][j] = 1;
else if (i == 0) record[i][j] = record[i][j - 1];
else if (j == 0) record[i][j] = record[i - 1][j];
else {
record[i][j] = record[i - 1][j] + record[i][j - 1];
}
}
}
return record[m - 1][n - 1];
}
};
5.不同路径 II
一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish”)。
现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径?
网格中的障碍物和空位置分别用 1 和 0 来表示。
![](https://i-blog.csdnimg.cn/blog_migrate/25cef219ba4a34d815d69e1acefe059a.jpeg)
分析:
在上一题的基础上加入了“不可通行”格子,需要在外面先判断格子能否同行
class Solution {
public:
int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
int m = obstacleGrid.size();
int n = obstacleGrid[0].size();
if (m == 0 || n == 0) return 1;
vector<vector<int>> record(m, vector<int>(n));
for (int i = 0; i <= m - 1; i++) {
for (int j = 0; j <= n - 1; j++) {
if (obstacleGrid[i][j] == 1) record[i][j] = 0;
else {
if (i == 0 && j == 0) record[i][j] = 1;
else if (i == 0) record[i][j] = record[i][j - 1];
else if (j == 0) record[i][j] = record[i - 1][j];
else {
record[i][j] = record[i - 1][j] + record[i][j - 1];
}
}
}
}
return record[m - 1][n - 1];
}
};
6.整数拆分
给定一个正整数 n ,将其拆分为 k 个 正整数 的和( k >= 2 ),并使这些整数的乘积最大化。
返回 你可以获得的最大乘积 。
与1中的剪绳子差不多
分析:设f(n)表示正整数n在此题下的最大值f(n)
假设将n分成k段,取其中任意一个设为i,则其他所有的子整数的和为n-i,
f(n)=f(i)*f(n-i)
class Solution {
public:
int integerBreak(int n) {
if (n == 0 || n == 1)return 0;
if (n == 2) return 1;
if (n == 3)return 2;
vector<int> record(n + 1);
record[0] = 0;
record[1] = 1;
record[2] = 2;
record[3] = 3;
for (int i = 4; i <= n; i++) {
int max_value = 0;
for (int j = 0; j <= i / 2; j++) {
max_value = max(max_value, record[j] * record[i - j]);
}
record[i] = max_value;
}
return record[n];
}
};
7.不同的二叉搜索树
给你一个整数 n ,求恰由 n 个节点组成且节点值从 1 到 n 互不相同的 二叉搜索树 有多少种?返回满足题意的二叉搜索树的种数。
示例 1:
![](https://i-blog.csdnimg.cn/blog_migrate/b168b65798008b78705935600faeda22.jpeg)
分析:
n个节点组成:一个根节点,左右子树
以四个节点为例,其根节点从1变至4.
当根节点为1时,左子书0个节点,右子树3个节点,共0+f(3)种子书
当根节点为2时,共f(1)+f(2)
当根节点为3,共f(2)+f(1)
当根节点为4,共f(3)+0
n个节点的二叉搜索树种树:
代码:
class Solution {
public:
int numTrees(int n) {
vector<int> record(n + 1);
if (n == 0)return 0;
if (n == 1)return 1;
record[0] = 1;
record[1] = 1;
for (int i = 2; i <= n; i++) {//从1个节点到n个节点
int count_total = 0;//记录i个不同子节点能组成的不同二叉搜索树种数
for (int j = 1; j <= i; j++) { //根节点从1变到i
int count = record[j - 1] * record[i - j];
count_total += count;
}
record[i] = count_total;
}
return record[n];
}
};