10.1斐波那契数列
题目
牛客网
思路
方法:迭代相加
斐波那契数列初始化第1项与第2项都是1,则根据公式第0项为0,可以按照斐波那契公式累加到第n项。
复杂度分析:
- 时间复杂度:O(n),其中n为输入的数,n次迭代
- 空间复杂度:O(1),常数级变量,没有其他额外辅助空间
1 | public class Solution { |
10.2矩形覆盖
题目
牛客网
思路
方法:动态规划
- n = 1的时候
- 只能竖着覆盖,1种
- n = 2的时候
- 可以竖着和横着覆盖,2种
- n = 3的时候
- 第三级竖着覆盖,用了一级,剩下 n = 2,有2种覆盖方法
- 第三级横着覆盖,用了两级,剩下 n = 1,有1种覆盖方法
- 总共有3种
- n = 4的时候
- 第四级竖着覆盖,用了一级,剩下 n = 3,有3种覆盖方法
- 第四级横着覆盖,用了两级,剩下 n = 2,有2种覆盖方法
- 总共有5种
- n = n的时候
- 第n级竖着覆盖,用了一级,剩下 n = n - 1,所以关注第 n - 1 种有几种覆盖方法
- 第n级横着覆盖,用了两级,剩下 n = n - 2,所以关注第 n - 2 种有几种覆盖方法
- 总和为两种情况的总和
复杂度分析:
- 时间复杂度:O(n),一次遍历
- 空间复杂度:O(1),常数级变量,没有其他额外辅助空间
1 | public class Solution { |
10.3跳台阶
题目
牛客网
思路
方法:动态规划
当 n = 1 时,只有一种跳法。
当 n = 2 时,有两种跳法。
跳 n 阶台阶,可以先跳 1 阶台阶,再跳 n-1 阶台阶;或者先跳 2 阶台阶,再跳 n-2 阶台阶。
复杂度分析:
- 时间复杂度:O(n),一次遍历
- 空间复杂度:O(1),常数级变量,没有其他额外辅助空间
1 | public class Solution { |
10.4变态跳台阶
题目
牛客网
思路
方法:动态规划
跳上 n-1 级台阶,可以从 n-2 级跳 1 级上去,也可以从 n-3 级跳 2 级上去…,那么1
f(n-1) = f(n-2) + f(n-3) + ... + f(0)
同样,跳上 n 级台阶,可以从 n-1 级跳 1 级上去,也可以从 n-2 级跳 2 级上去… ,那么1
f(n) = f(n-1) + f(n-2) + ... + f(0)
综上可得1
f(n) - f(n-1) = f(n-1)
即1
f(n) = 2*f(n-1)
复杂度分析:
- 时间复杂度:O(n),一次遍历
- 空间复杂度:O(1),常数级变量,没有其他额外辅助空间
1 | public class Solution { |
42.连续子数组的最大和
题目
牛客网
思路
方法:动态规划
设动态规划列表 dp,dp[i] 代表以元素 array[i] 为结尾的连续子数组最大和。
状态转移方程: dp[i] = Math.max(dp[i - 1] + array[i], array[i]);
dp[i]与dp[ i - 1]是否大于0相关:
- dp[i] = dp[i - 1] + array[i],dp[i - 1] > 0
- dp[i] = array[i] ,dp[i - 1] <= 0
假设:在看到这种思路时提出了一个问题,那就是假设:dp[i - 1] = dp[i - 2] + 正整数,那么当dp[i - 1] < 0 的时候,为什么要抛弃array[i]之前的所有数呢?毕竟array[i] + 正整数也是比array[i]要更大的。
证明:当dp[i - 1] = dp[i - 2] + 正整数,那么必定有dp[i - 2] > 0,那么dp[i - 1]一定也 > 0,所以以上假设中dp[i - 1] < 0不成立,所以以上问题不存在。
复杂度分析:
- 时间复杂度:O(n),一次遍历
- 空间复杂度:O(1),常数级变量,没有其他额外辅助空间
1 | public class Solution { |
47.礼物的最大价值
题目
牛客网
思路
方法:动态规划
如果我们现在已经身处最右下角的一个格子,获取了这个礼物,那我们肯定是加上来自左边累计的最大礼物价值与来自上边累计的最大礼物价值的较大值,这样我们能获取的礼物价值才会更大,因此我们用dp[i][j]表示从左上角到第i行第j列的格子总共能获取的最大价值,因此转移方程为:
1 | dp[i][j] = grid[i][j] + max(dp[i−1][j], dp[i][j−1])) |
复杂度分析:
- 时间复杂度:O(mn),其中m、n分别为矩阵的边长,遍历整个矩阵
- 空间复杂度:O(1),原数组中修改,无额外辅助空间
1 | public class Solution { |
48.最长不含重复字符的子字符串
题目
牛客网
思路
方法:动态规划+哈希表
如果对于某个前面的子串,如果我们新加入一个字符,与前面的都不重复,那么最长无重复子串肯定就是在前面的基础上加1,如果与前面重复了,那就是当前位置减去它重复之前字符出现的位置的长度。因此我们使用动态规划递推。
复杂度分析:
- 时间复杂度:O(n),其中n为字符串长度,遍历一次字符串
- 空间复杂度:O(n),辅助数组dp的大小为字符串长度,哈希表的最大空间为字符串长度
1 | import java.util.*; |
49.丑数
题目
牛客网
思路
方法:动态规划
我们知道丑数是由1开始的每个丑数依次乘上2、3、5得到,而我们每次只需要在其中找到最小的一个,一共找n次即可。我们可以用i、j、k三个下标表示在已经找到的丑数中那个数分别被乘2、乘3、乘5有无被记录过,然后依次找n个数字就可以了。
复杂度分析:
- 时间复杂度:O(n),只需要遍历一次
- 空间复杂度:O(n),记录丑数的数组最大长度为n
1 | import java.util.*; |
66.构建乘积数组
题目
题目主要信息:
- 给定一个数组A,要求返回数组B,数组B每个元素等于数组A所有元素除了对应下标以外的全部元素的乘积
- 即B[i]=A[0]∗A[1]∗…∗A[i−1]∗A[i+1]∗…∗A[n−1]
- 程序不能使用除法
1 | 输入:[1,2,3,4,5] |
思路
方法:双向遍历
矩阵中由对角线1将其分成了上三角和下三角。我们先看下三角,如果我们累乘的时候,B[1]是在B[0]的基础上乘了新增的一个A[0],B[2]是在B[1]的基础上乘了新增的一个A[1],那我们可以遍历数组的过程中不断将数组B的前一个数与数组A的前一个数相乘就得到了下三角中数组B的当前数。同理啊,我们在上三角中,用一个变量存储从右到左的累乘,每次只会多乘上一个数字。这样,两次遍历就可以解决。
复杂度分析:
- 时间复杂度:O(n),其中n为数组A的长度,遍历两次数组
- 空间复杂度:O(n),数组B为返回必要空间,不属于额外空间
1 | public class Solution { |