leetcode
[hot] 45. 跳跃游戏 II
题目
给定一个非负整数数组,你最初位于数组的第一个位置。数组中的每个元素代表你在该位置可以跳跃的最大长度。你的目标是使用最少的跳跃次数到达数组的最后一个位置。
示例:
输入: [2,3,1,1,4]
输出: 2
解释: 跳到最后一个位置的最小跳跃数是 2。
从下标为 0 跳到下标为 1 的位置,跳 1 步,然后跳 3 步到达数组的最后一个位置。
题解
遍历过程中记录从当前位置能跳的最远位置,并记录最少的跳跃次数,示意图如下所示:
示例代码如下所示:
class Solution {
public:
int jump(vector<int>& nums) {
int max_pos = 0;
int end = 0; // 记录上一次能跳到的最远距离
int steps = 0; // 步数记录
// 题目为能否跳到最后一个位置,因而不能遍历到最后一个位置,否则会多跳一次
for (int i = 0; i < nums.size() - 1; ++i) {
max_pos = max(max_pos, i + nums[i]);
if (i == end) { // i到达了上次的最远距离,则更新end和step
end = max_pos;
++steps;
}
}
return steps;
}
};
复杂度
时间复杂度:
O
(
n
)
O(n)
O(n)
空间复杂度:
O
(
1
)
O(1)
O(1)
[hot] 55. 跳跃游戏
题目
给定一个非负整数数组 nums ,你最初位于数组的 第一个下标 。数组中的每个元素代表你在该位置可以跳跃的最大长度。判断你是否能够到达最后一个下标。
示例 1:
输入:nums = [2,3,1,1,4]
输出:true
解释:可以先跳 1 步,从下标 0 到达下标 1, 然后再从下标 1 跳 3 步到达最后一个下标。
示例 2:
输入:nums = [3,2,1,0,4]
输出:false
解释:无论怎样,总会到达下标为 3 的位置。但该下标的最大跳跃长度是 0 , 所以永远不可能到达最后一个下标。
题解
贪心算法解决问题,记录在当前位置能够跳跃的最远距离rightmost,到最后看是否能够跳到末尾。注意题意是"能否到达最后一个下标",那么需要遍历的其实是[0, length - 1),这时length = 1时需要特殊处理,直接返回true,示例代码如下所示:
class Solution {
public:
bool canJump(vector<int>& nums) {
int length = nums.size();
if (length == 1) {
return true;
}
int rightmost = 0;
for (int i = 0; i < length - 1; ++i) {
// i之前的最远跳不到i,出现断层,直接返回false
if (i > rightmost) {
return false;
}
rightmost = max(rightmost, i + nums[i]);
if (rightmost >= length - 1) {
return true;
}
}
return false;
}
};
复杂度
时间复杂度:
O
(
n
)
O(n)
O(n)
空间复杂度:
O
(
1
)
O(1)
O(1)
关键点
到达数组最后一个位置,但是不是跳完,这个一定要注意
[hot] 56. 合并区间
题目
以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] = [starti, endi] 。请你合并所有重叠的区间,并返回一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间。
示例 1:
输入:intervals = [[1,3],[2,6],[8,10],[15,18]]
输出:[[1,6],[8,10],[15,18]]
解释:区间 [1,3] 和 [2,6] 重叠, 将它们合并为 [1,6].
示例 2:
输入:intervals = [[1,4],[4,5]]
输出:[[1,5]]
解释:区间 [1,4] 和 [4,5] 可被视为重叠区间。
题解
常规思路合并,边遍历边合并,示例代码如下所示:
class Solution {
public:
vector<vector<int>> merge(vector<vector<int>>& intervals) {
sort(intervals.begin(), intervals.end());
vector<vector<int>> res;
for (auto& elem: intervals) {
auto& L = elem[0];
auto& R = elem[1];
if (res.empty() || res.back()[1] < L) {
res.emplace_back(elem);
} else {
res.back()[1] = max(res.back()[1], R);
}
}
return res;
}
};
复杂度
时间复杂度:
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn),排序的时间复杂度
空间复杂度:
O
(
1
)
O(1)
O(1)
关键点
待插入区间和数组中最后一个区间有重合时,需要取两者区间右侧元素的最大值
57. 插入区间
题目
给你一个 无重叠的 ,按照区间起始端点排序的区间列表。在列表中插入一个新的区间,你需要确保列表中的区间仍然有序且不重叠(如果有必要的话,可以合并区间)。
示例 1:
输入:intervals = [[1,3],[6,9]], newInterval = [2,5]
输出:[[1,5],[6,9]]
示例 2:
输入:intervals = [[1,2],[3,5],[6,7],[8,10],[12,16]], newInterval = [4,8]
输出:[[1,2],[3,10],[12,16]]
解释:这是因为新的区间 [4,8] 与 [3,5],[6,7],[8,10] 重叠。
示例 3:
输入:intervals = [], newInterval = [5,7]
输出:[[5,7]]
示例 4:
输入:intervals = [[1,5]], newInterval = [2,3]
输出:[[1,5]]
示例 5:
输入:intervals = [[1,5]], newInterval = [2,7]
输出:[[1,7]]
题解
一图抵千言,示意图如下所示,S像一个漏斗一样,来区分区间列表中的每一个区间,具体讲解见leetcode官方讲解。
示例代码如下所示:
class Solution {
public:
vector<vector<int>> insert(vector<vector<int>>& intervals, vector<int>& newInterval) {
int left = newInterval[0];
int right = newInterval[1];
bool placed = false;
vector<vector<int>> ans;
for (const auto& interval: intervals) {
if (interval[0] > right) {
// 在插入区间的右侧且无交集
if (!placed) {
ans.push_back({left, right});
placed = true;
}
ans.push_back(interval);
}
else if (interval[1] < left) {
// 在插入区间的左侧且无交集
ans.push_back(interval);
}
else {
// 与插入区间有交集,计算它们的并集
left = min(left, interval[0]);
right = max(right, interval[1]);
}
}
if (!placed) {
ans.push_back({left, right});
}
return ans;
}
};
复杂度
时间复杂度:
O
(
n
)
O(n)
O(n)
空间复杂度:
O
(
1
)
O(1)
O(1)
关键点
需要想到一种拿到区间的第一个元素后,就无处可插的情况。