持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第10天,点击查看活动详情
55. 跳跃游戏
思路
(贪心) O(n)
从前往后遍历nums数组,记录我们能跳到的最远位置j,如果存在我们不能跳到的下标i,返回false即可,否则返回true。
具体过程如下:
-
1、定义一个
j变量用来记录我们可以跳到的最远位置,初始化j = 0。 -
2、遍历整个
nums[]数组,i表示当前需要跳到的下标位置。- 若
j < i,说明下标i不可达,则返回false; - 否则,说明
i可达,则我们以i为起点更新可以跳到的最远位置j,即j = max(j, i + nums[i]);
- 若
-
3、如果可以遍历完整个数组,说明可以到达最后一个下标
i,我们返回true。
时间复杂度分析: 只遍历一次数组,因此时间复杂度为O(n)。
c++代码
class Solution {
public:
bool canJump(vector<int>& nums) {
for(int i = 0, j = 0; i < nums.size(); i++){
if(j < i) return false;
j = max(j, i + nums[i]);
}
return true;
}
};
56. 合并区间
思路
(数组,排序) O(nlogn)
1、将所有的区间按照左端点从小到大排序
2、定义区间左端点
l = a[0][0],右端点r = a[0][1](等价于两个左右指针),我们从前往后遍历每个区间:
-
如果当前区间和上一个区间没有交集,也就是说当前区间的左端点
>上一个区间的右端点,即a[i][0] > r,说明上一个区间独立,我们将上一个区间的左右端点[l,r]加入答案数组中,并更新左端点l,右端点r为当前区间的左右端点,即l = a[i][0], r = a[i][1]。始终维持
l和r为最新独立区间的左右端点。
-
如果当前区间和上一个区间有交集,即当前区间的左端点
<=上一个区间的右端点,我们让左端点l保持不变,右端点r更新为max(r,a[i][1]),进行区间的合并 。
3、最后再将最后一个合并或者未合并的独立区间[l,r]加入答案数组中。
时间复杂度分析: 遍历区间数组的时间为O(n),对区间数组进行排序的时间复杂度为O(nlogn) ,因此总的时间复杂度为O(nlogn)
c++代码
class Solution {
public:
vector<vector<int>> merge(vector<vector<int>>& a) {
vector<vector<int>> res;
sort(a.begin(), a.end()); //按照左端点排序
int l = a[0][0], r = a[0][1];
for(int i = 1; i < a.size(); i++){
if(a[i][0] > r){
res.push_back({l, r});
l = a[i][0], r = a[i][1];
}else{
r = max(r, a[i][1]);
}
}
res.push_back({l, r});
return res;
}
};
62. 不同路径
思路
(动态规划) O(m*n)
状态表示: f[i,j]表示从(0,0)走到(i,j)的所有不同路径的方案数。那么,f[m-1][n-1]就表示从网格左上角到网格右下角的所有不同路径的方案数,即为答案。
状态转移:
由于限制了只能向下走或者向右走,因此到达(i,j)有两条路径
- 从上方转移过来,
f[i][j] = f[i-1][j]; - 从左方转移过来,
f[i][j] = f[i][j-1];
因此,状态计算方程为: f[i][j] = f[i-1][j] + f[i][j-1] , 将向右和向下两条路径的方案数相加起来。
初始化条件: f[0][0] = 1,从(0,0)到达(0,0)只有一条路径。
分析图示:
时间复杂度分析: O(m*n),其中 m和 n分别是网格的行数和列数 。
c++代码
class Solution {
public:
int uniquePaths(int m, int n) {
if(!n || !m) return 0;
vector<vector<int>>f(m + 1, vector<int>(n + 1));
f[0][0] = 1;
for(int i = 0; i < m; i++)
for(int j = 0; j < n; j++){
if(!i && !j) continue;
if(i) f[i][j] += f[i - 1][j];
if(j) f[i][j] += f[i][j - 1];
}
return f[m - 1][n - 1];
}
};