1. 爬楼梯
假设你正在爬楼梯。需要 n 步你才能到达楼顶。
每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
注意:给定 n 是一个正整数。
示例 1:
输入: 2
输出: 2
解释: 有两种方法可以爬到楼顶。
1. 1 步 + 1 步
2. 2 步
示例 2:
输入: 3
输出: 3
解释: 有三种方法可以爬到楼顶。
1. 1 步 + 1 步 + 1 步
2. 1 步 + 2 步
3. 2 步 + 1 步
解:
利用枚举抽象出一般表达式
当n=0时,f(n)=1
n=1,f(n)=1
n=2,f(n)=2
n=3,fn=3
n=4,fn=5
f(2)=f(1)+f(0)
f(3)=f(2)+f(1)
f(4)=f(3)+f(2)
.......
f(n)=f(n-2)+f(n-1)
递归:
class Solution {
public:
int climbStairs(int n) {
//f(n)=f(n-2)+f(n-1)
if (n == 0) {
return 0;
}
if (n == 1) {
return 1;
}
if (n == 2) {
return 2;
}
return climbStairs(n - 1) + climbStairs(n - 2);
}
};
非递归
class Solution {
public:
int climbStairs(int n) {
if(n<2) return 1;
int a1=1,a2=1;
for(int i=2;i<n;i++){
swap(a2,a1);
a2=a1+a2;
}
return a1+a2;
}
};
2.不同路径
一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。
问总共有多少条不同的路径?
例如,上图是一个7 x 3 的网格。有多少可能的路径?
说明:m 和 n 的值均不超过 100。
示例 1:
输入: m = 3, n = 2
输出: 3
解释:
从左上角开始,总共有 3 条路径可以到达右下角。
1. 向右 -> 向右 -> 向下
2. 向右 -> 向下 -> 向右
3. 向下 -> 向右 -> 向右
示例 2:
输入: m = 7, n = 3
输出: 28
思路:
这是一道典型的动态规划问题,使用一个二维数组ans记忆到达每一点可行的走法总数。首先将左边界点和上边界点初始化为1,因为机器人起始与(0,0),左边界点和上边界点的走法只有1种。接下来的每一点(x,y),可以由(x-1,y)向右走或是(x,y-1)向下走来到达,因此在(x,y)这一点可到达的方法有ans[x-1][y]+ans[x][y-1]种,到达终点的方法则是ans最后一个点的数据。
class Solution {
public:
int uniquePaths(int m, int n) {
vector<vector<int>> a(m,vector<int>(n,0));
if(m==0||n==0)
return 0;
for(int i=0;i<m;i++)
a[i][0]=1;
for(int i=0;i<n;i++)
a[0][i]=1;
for(int i=1;i<m;i++)
for(int j=1;j<n;j++)
a[i][j]=a[i-1][j]+a[i][j-1];
return a[m-1][n-1];
}
};
3.跳跃游戏
给定一个非负整数数组,你最初位于数组的第一个位置。
数组中的每个元素代表你在该位置可以跳跃的最大长度。
判断你是否能够到达最后一个位置。
示例 1:
输入: [2,3,1,1,4]
输出: true
解释: 从位置 0 到 1 跳 1 步, 然后跳 3 步到达最后一个位置。
示例 2:
输入: [3,2,1,0,4]
输出: false
解释: 无论怎样,你总会到达索引为 3 的位置。但该位置的最大跳跃长度是 0 , 所以你永远不可能到达最后一个位置。
思路:这步能跳多远取决于两个因素:当前脚力C与之前的剩余步数L。当前脚力C为非负整数,剩余步数L与前一步的脚力和剩余步数有关,其关系为。
L[i]=max(C[i],L[i])-1;
如果之前剩余步数和当前的脚力都不足以买过前面这个坎,就说明永远无法走到最后一步。
因此当前脚力时直接可以知道的,因此剩余脚力是一个从第一步开始维护的动态规划量;
class Solution {
public:
bool canJump(vector<int>& nums)
{
if(nums.size()<=1)
return true;
int L=0;
int C=nums[0];
for(int i=1;i<nums.size();i++)
{
L=max(L,nums[i-1])-1;
if(L<0)
return false;
}
return L>=0;
}
};
4. 数组串查找
给定一个无序的整数数组,找到其中最长上升子序列的长度。
示例:
输入:[10,9,2,5,3,7,101,18]
输出: 4 解释: 最长的上升子序列是[2,3,7,101],
它的长度是4
。
说明:
- 可能会有多种最长上升子序列的组合,你只需要输出对应的长度即可。
- 你算法的时间复杂度应该为 O(n2) 。
进阶: 你能将算法的时间复杂度降低到 O(n log n) 吗?
思路 : 我们维护一个一位数组dp,其中dp[i]表示达到i位置时剩余的步数,那么难点就是推导状态转移方程啦。我们想啊,到达当前位置的剩余步数跟什么有关呢,其实是跟上一个位置的剩余步数和上一个位置的跳力有关,这里的跳力就是原数组中每个位置的数字,因为其代表了以当前位置为起点能到达的最远位置。所以当前位置的剩余步数(dp值)和当前位置的跳力中的较大那个数决定了当前能到的最远距离,而下一个位置的剩余步数(dp值)就等于当前的这个较大值减去1,因为需要花一个跳力到达下一个位置,所以我们就有状态转移方程了:dp[i] = max(dp[i - 1], nums[i - 1]) - 1,如果当某一个时刻dp数组的值为负了,说明无法抵达当前位置,则直接返回false,最后我们判断dp数组最后一位是否为非负数即可知道是否能抵达该位置,代码如下:
class Solution {
public:
int lengthOfLIS(vector<int>& nums) {
vector<int> dp(nums.size(), 1);
int res = 0;
for (int i = 0; i < nums.size(); ++i) {
for (int j = 0; j < i; ++j) {
if (nums[i] > nums[j]) {
dp[i] = max(dp[i], dp[j] + 1);
}
}
res = max(res, dp[i]);
}
return res;
}
};