给你一个非负整数数组 nums
,你最初位于数组的 第一个下标 。数组中的每个元素代表你在该位置可以跳跃的最大长度。
判断你是否能够到达最后一个下标,如果可以,返回 true
;否则,返回 false
。
思路:
贪心算法局部最优解:每次取最大跳跃步数(取最大覆盖范围),整体最优解:最后得到整体最大覆盖范围,看是否能到终点。
代码如下:
class Solution { public: bool canJump(vector<int>& nums) { int cover = 0; if (nums.size() == 1) return true; // 只有一个元素,就是能达到 for (int i = 0; i <= cover; i++) { // 注意这里是小于等于cover cover = max(i + nums[i], cover); if (cover >= nums.size() - 1) return true; // 说明可以覆盖到终点了 } return false; } };
给定一个长度为 n
的 0 索引整数数组 nums
。初始位置为 nums[0]
。
每个元素 nums[i]
表示从索引 i
向前跳转的最大长度。换句话说,如果你在 nums[i]
处,你可以跳转到任意 nums[i + j]
处:
0 <= j <= nums[i]
i + j < n
返回到达 nums[n - 1]
的最小跳跃次数。生成的测试用例可以到达 nums[n - 1]
。
思路:
局部最优:当前可移动距离尽可能多走,如果还没到终点,步数再加一。整体最优:一步尽可能多走,从而达到最少步数。
那就是贪在每次要走最大距离,那么就需要不断更新在(i到现在可走最大距离的下标的范围内)下一步的最远距离能到哪里。
代码如下:
class Solution { public: int jump(vector<int>& nums) { int curDistance = 0; // 当前覆盖的最远距离下标 int ans = 0; // 记录走的最大步数 int nextDistance = 0; // 下一步覆盖的最远距离下标 for (int i = 0; i < nums.size() - 1; i++) { // 注意这里是小于nums.size() - 1,这是关键所在 nextDistance = max(nums[i] + i, nextDistance); // 更新下一步覆盖的最远距离下标 if (i == curDistance) { // 遇到当前覆盖的最远距离下标 curDistance = nextDistance; // 更新当前覆盖的最远距离下标 ans++; } } return ans; } };
有一些球形气球贴在一堵用 XY 平面表示的墙面上。墙面上的气球记录在整数数组 points
,其中points[i] = [xstart, xend]
表示水平直径在 xstart
和 xend
之间的气球。你不知道气球的确切 y 坐标。
一支弓箭可以沿着 x 轴从不同点 完全垂直 地射出。在坐标 x
处射出一支箭,若有一个气球的直径的开始和结束坐标为 x
start
,x
end
, 且满足 xstart ≤ x ≤ x
end
,则该气球会被 引爆 。可以射出的弓箭的数量 没有限制 。 弓箭一旦被射出之后,可以无限地前进。
给你一个数组 points
,返回引爆所有气球所必须射出的 最小 弓箭数 。
思路:
局部最优:当气球出现重叠,一起射,所用弓箭最少。全局最优:把所有气球射爆所用弓箭最少。
为了让气球尽可能的重叠,需要根据气球起始位置对数组进行排序。然后遍历气球,如果当前气球的终点位置大于下一个气球的起始位置,那就是重叠了。
紧接着就要更新遍历过的气球共同的终点位置,要么是下一个气球的终点(当前气球包含了下一个气球),要么是当前气球的终点。
代码如下:
class Solution { private: static bool cmp(const vector<int>& a, const vector<int>& b) { return a[0] < b[0]; } public: int findMinArrowShots(vector<vector<int>>& points) { if (points.size() == 0) return 0; sort(points.begin(), points.end(), cmp); int result = 1; // points 不为空至少需要一支箭 for (int i = 1; i < points.size(); i++) { if (points[i][0] > points[i - 1][1]) { // 气球i和气球i-1不挨着,注意这里不是>= result++; // 需要一支箭 } else { // 气球i和气球i-1挨着 points[i][1] = min(points[i - 1][1], points[i][1]); // 更新重叠气球最小右边界 } } return result; } };
给定一个区间的集合 intervals
,其中 intervals[i] = [starti, endi]
。返回 需要移除区间的最小数量,使剩余区间互不重叠 。
注意 只在一点上接触的区间是 不重叠的。例如 [1, 2]
和 [2, 3]
是不重叠的。
示例 1:
输入: intervals = [[1,2],[2,3],[3,4],[1,3]] 输出: 1 解释: 移除 [1,3] 后,剩下的区间没有重叠。
示例 2:
输入: intervals = [ [1,2], [1,2], [1,2] ] 输出: 2 解释: 你需要移除两个 [1,2] 来使剩下的区间没有重叠。
示例 3:
输入: intervals = [ [1,2], [2,3] ] 输出: 0 解释: 你不需要移除任何区间,因为它们已经是无重叠的了。
思路:
这道题和上面气球那道题是一样的,局部最优就是尽可能让数组重叠,对其初始位置排序。全局最优是让去除的数组越少越好。
代码如下:
class Solution { private: static bool cmp(const vector<int>& a,const vector<int>& b){ return a[0]<b[0]; } public: int eraseOverlapIntervals(vector<vector<int>>& intervals) { int res=0; sort(intervals.begin(),intervals.end(),cmp); for(int i=1;i<intervals.size();i++){ if(intervals[i][0]<intervals[i-1][1]){ res++; intervals[i][1]=min(intervals[i-1][1],intervals[i][1]); } } return res; } };
给你一个字符串 s
。我们要把这个字符串划分为尽可能多的片段,同一字母最多出现在一个片段中。
注意,划分结果需要满足:将所有划分结果按顺序连接,得到的字符串仍然是 s
。
返回一个表示每个字符串片段的长度的列表。
示例 1:
输入:s = "ababcbacadefegdehijhklij" 输出:[9,7,8] 解释: 划分结果为 "ababcbaca"、"defegde"、"hijhklij" 。 每个字母最多出现在一个片段中。 像 "ababcbacadefegde", "hijhklij" 这样的划分是错误的,因为划分的片段数较少。
示例 2:
输入:s = "eccbbbbdec" 输出:[10]
思路:
每次遇到一个字符时,贪心地选择当前字符所在的片段的 最远边界
注意rfind的用法。
代码如下:
class Solution { public: vector<int> partitionLabels(string s) { vector<int> res; int rboundary = 0; int start = 0; for (int i = 0; i < s.size(); ++i) { // 更新右边界为当前字符最后出现的位置 rboundary = max(rboundary, (int)s.rfind(s[i]));//(int)是把size_t类型转成int // 如果当前字符是当前片段的右边界,说明这一段可以划分 if (i == rboundary) { res.push_back(i - start + 1); start = i + 1; } } return res; } };
如果不会用rfind来找边界,就先统计每个字母最后出现的位置。
代码如下:
class Solution { public: vector<int> partitionLabels(string S) { int hash[27] = {0}; // i为字符,hash[i]为字符出现的最后位置 for (int i = 0; i < S.size(); i++) { // 统计每一个字符最后出现的位置 hash[S[i] - 'a'] = i;//遇到相同的字母会更新该字母最后出现的位置 } vector<int> result; int left = 0; int right = 0; for (int i = 0; i < S.size(); i++) { right = max(right, hash[S[i] - 'a']); // 找到字符出现的最远边界 if (i == right) { result.push_back(right - left + 1); left = i + 1; } } return result; } };
另外,还可以强行按照气球和划分字母区间的思路来做。
就是要自己构造每个字母对应长度的数组。
代码如下:
class Solution { public: static bool cmp(vector<int> &a, vector<int> &b) { return a[0] < b[0]; } // 记录每个字母出现的区间 vector<vector<int>> countLabels(string s) { vector<vector<int>> hash(26, vector<int>(2, INT_MIN)); vector<vector<int>> hash_filter; for (int i = 0; i < s.size(); ++i) { if (hash[s[i] - 'a'][0] == INT_MIN) { hash[s[i] - 'a'][0] = i; } hash[s[i] - 'a'][1] = i; } // 去除字符串中未出现的字母所占用区间 for (int i = 0; i < hash.size(); ++i) { if (hash[i][0] != INT_MIN) { hash_filter.push_back(hash[i]); } } return hash_filter; } vector<int> partitionLabels(string s) { vector<int> res; // 这一步得到的 hash 即为无重叠区间题意中的输入样例格式:区间列表 // 只不过现在我们要求的是区间分割点 vector<vector<int>> hash = countLabels(s); // 按照左边界从小到大排序 sort(hash.begin(), hash.end(), cmp); // 记录最大右边界 int rightBoard = hash[0][1]; int leftBoard = 0; for (int i = 1; i < hash.size(); ++i) { // 由于字符串一定能分割,因此, // 一旦下一区间左边界大于当前右边界,即可认为出现分割点 if (hash[i][0] > rightBoard) { res.push_back(rightBoard - leftBoard + 1); leftBoard = hash[i][0]; } rightBoard = max(rightBoard, hash[i][1]); } // 最右端 res.push_back(rightBoard - leftBoard + 1); return res; } };
以数组 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] 可被视为重叠区间。
思路:
和之前思路一样,也是先排序。
要注意的是:erase会改变数组每个数前移一位,所以每次erase完要i–才能继续遍历。
代码如下:
class Solution { private: static bool cmp(const vector<int>& a, const vector<int>& b) { return a[0] < b[0]; } public: vector<vector<int>> merge(vector<vector<int>>& intervals) { sort(intervals.begin(), intervals.end(), cmp); for (int i = 1; i < intervals.size(); i++) { if (intervals[i][0] <= intervals[i - 1][1]) { if (intervals[i][1] < intervals[i - 1][1]) { intervals[i][1] = intervals[i - 1][1]; } intervals[i][0] = intervals[i - 1][0]; intervals.erase(intervals.begin() + i - 1); i--; } } return intervals; } };
暂无评论