供自己复习,一篇10题左右
- 1.分发饼干
- 2.分发糖果
- 3.跳跃游戏I
- 4.跳跃游戏II
- 5.合并区间
- 6.无重叠区间
- 7.划分字母区间
- 8.加油站
1.分发饼干
假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。
对每个孩子 i,都有一个胃口值 g[i],这是能让孩子们满足胃口的饼干的最小尺寸;并且每块饼干 j,都有一个尺寸 s[j] 。如果 s[j] >= g[i],我们可以将这个饼干 j 分配给孩子 i ,这个孩子会得到满足。你的目标是尽可能满足越多数量的孩子,并输出这个最大数值。
示例 1:
- 输入: g = [1,2,3], s = [1,1]
- 输出: 1 解释:你有三个孩子和两块小饼干,3 个孩子的胃口值分别是:1,2,3。虽然你有两块小饼干,由于他们的尺寸都是 1,你只能让胃口值是 1 的孩子满足。所以你应该输出 1。
这里的局部最优就是大饼干喂给胃口大的,充分利用饼干尺寸喂饱一个,全局最优就是喂饱尽可能多的小孩。
仔细阅读题目的要求,你的目标是尽可能满足越多数量的孩子,并输出这个最大数值。
所以必须是先遍历胃口(孩子),给再遍历饼干
不能颠倒!!!!
class Solution {
public:int findContentChildren(vector<int>& g, vector<int>& s) {sort(g.begin(), g.end());sort(s.begin(), s.end());int index = s.size() - 1;int result = 0;for(int i = g.size() - 1; i >= 0; i --){if(index >= 0 && g[i] <= s[index]){result ++;index --;}}return result;}
};
2.分发糖果
老师想给孩子们分发糖果,有 N 个孩子站成了一条直线,老师会根据每个孩子的表现,预先给他们评分。
你需要按照以下要求,帮助老师给这些孩子分发糖果:
- 每个孩子至少分配到 1 个糖果。
- 相邻的孩子中,评分高的孩子必须获得更多的糖果。
那么这样下来,老师至少需要准备多少颗糖果呢?
示例 1:
- 输入: [1,0,2]
- 输出: 5
- 解释: 你可以分别给这三个孩子分发 2、1、2 颗糖果。
先确定右边评分大于左边的情况(也就是从前向后遍历)
再确定左边大于右边的情况,但是此时不能再从前向后遍历了!!
如图的解释,判断左边大于右边,从前向后遍历的时候,得出的candyVec序列是1 2 1 2 1 1 1
如果还是从前向后遍历,判断右边大于左边,就会得到 1 2 1 2 2 2 2 的序列,从评分为5的孩子看来,应该比4高但是却没有得到更多的糖
根本原因就是,图上的3 5 4,判断加不加糖,左和右是岔开的,不能一样的遍历判断
这样逻辑会出问题。
第二个,就是从后向前遍历的时候,candVec[i]的选取问题,不能直接取candyVec[i + 1] + 1,因为有可能candyVec本身就比他大,再加的话不满足题目要求的最少糖果,所以candyVec[i] = max(candyVec[i], candyVec[i + 1] + 1)
class Solution {
public:int candy(vector<int>& ratings) {vector<int> candyVec(ratings.size(), 1);// 从前向后for (int i = 1; i < ratings.size(); i++) {if (ratings[i] > ratings[i - 1]) candyVec[i] = candyVec[i - 1] + 1;}// 从后向前for (int i = ratings.size() - 2; i >= 0; i--) {if (ratings[i] > ratings[i + 1] ) {candyVec[i] = max(candyVec[i], candyVec[i + 1] + 1);}}// 统计结果int result = 0;for (int i = 0; i < candyVec.size(); i++) result += candyVec[i];return result;}
};
3.跳跃游戏I
给你一个非负整数数组 nums
,你最初位于数组的 第一个下标 。数组中的每个元素代表你在该位置可以跳跃的最大长度。判断你是否能够到达最后一个下标,如果可以,返回 true
;否则,返回 false
。
题目只关心能不能跳到终点,所以不用关系要跳几步,只要最后能覆盖住终点就行
贪心算法局部最优解:每次取最大跳跃步数(取最大覆盖范围),整体最优解:最后得到整体最大覆盖范围,看是否能到终点。
定义cover为每次能跳的步长,跳完当前步长和,步跳完当前步长后能跳的步长,做对比,取最大的!
cover可能会大于数组长度,所以要及时return!!
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 = max(i + nums[i], cover);if (cover >= nums.size() - 1) return true; }return false;}
};
4.跳跃游戏II
给定一个长度为 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]
。
不能真的能跳多远就跳多远,那样就不知道下一步最远能跳到哪里了。
所以真正解题的时候,要从覆盖范围出发,不管怎么跳,覆盖范围内一定是可以跳到的,以最小的步数增加覆盖范围,覆盖范围一旦覆盖了终点,得到的就是最少步数!
这里需要统计两个覆盖范围,当前这一步的最大覆盖和下一步最大覆盖。
如果移动下标达到了当前这一步的最大覆盖最远距离了,还没有到终点的话,那么就必须再走一步来增加覆盖范围,直到覆盖范围覆盖了终点。
通俗点说,
第一步是要知道当前最远能跳到哪里,这里只需要知道最远能跳到哪里,中间没有操作
第二步是要知道以当前的跳的基础,下一步能跳到哪里,虽然是以当前的跳为基础,但是这里是个范围,在第一步没跳到第一步落地这个范围里面,找到第二步的最大覆盖
更新:当前覆盖的更新,是拿每一次next的值去更
class Solution {
public:int jump(vector<int>& nums) {if(nums.size() <= 1) return 0;int cur = 0;int next = 0;int ans = 0;for(int i = 0; i < nums.size(); i ++){next = max(nums[i] + i, next);if(i == cur){ans ++;cur = next;if(next >= nums.size() - 1) break;}}return ans;}
};
5.合并区间
给出一个区间的集合,请合并所有重叠的区间。
示例 1:
- 输入: intervals = [[1,3],[2,6],[8,10],[15,18]]
- 输出: [[1,6],[8,10],[15,18]]
- 解释: 区间 [1,3] 和 [2,6] 重叠, 将它们合并为 [1,6].
class Solution {
public:vector<vector<int>> merge(vector<vector<int>>& intervals) {sort(intervals.begin(), intervals.end(), [](const vector<int> a, const vector<int> b){return a[0] < b[0];});vector<vector<int>> result;result.push_back(intervals[0]);for(int i = 0; i < intervals.size(); i ++){if(intervals[i][0] <= result.back()[1]){ // 区间重叠result.back()[1] = max(intervals[i][1], result.back()[1]);}else{result.push_back(intervals[i]); // 区间不重叠 }}return result;}
};
6.无重叠区间
给定一个区间的集合,找到需要移除区间的最小数量,使剩余区间互不重叠。
注意: 可以认为区间的终点总是大于它的起点。 区间 [1,2] 和 [2,3] 的边界相互“接触”,但没有相互重叠。
示例 1:
- 输入: [ [1,2], [2,3], [3,4], [1,3] ]
- 输出: 1
- 解释: 移除 [1,3] 后,剩下的区间没有重叠。
按照右边界排序!!!
class Solution {
public:static bool cmp(const vector<int> a, const vector<int> b){return a[1] < b[1];}int eraseOverlapIntervals(vector<vector<int>>& intervals) {if(intervals.size() == 0) return 0;sort(intervals.begin(), intervals.end(), cmp);int end = intervals[0][1];int count = 1;for(int i = 1; i < intervals.size(); i ++){if(intervals[i][0] >= end){count ++;end = intervals[i][1];}}return intervals.size() - count;}
};
7.划分字母区间
字符串 S 由小写字母组成。我们要把这个字符串划分为尽可能多的片段,同一字母最多出现在一个片段中。返回一个表示每个字符串片段的长度的列表。
示例:
- 输入:S = “ababcbacadefegdehijhklij”
- 输出:[9,7,8] 解释: 划分结果为 “ababcbaca”, “defegde”, “hijhklij”。 每个字母最多出现在一个片段中。 像"ababcbacadefegde", “hijhklij” 的划分是错误的,因为划分的片段数较少。
提示:
- S的长度在[1, 500]之间。
- S只包含小写字母 ‘a’ 到 ‘z’
在遍历的过程中相当于是要找每一个字母的边界,如果找到之前遍历过的所有字母的最远边界,说明这个边界就是分割点了。此时前面出现过所有字母,最远也就到这个边界了。
可以分为如下两步:
- 统计每一个字符最后出现的位置
- 从头遍历字符,并更新字符的最远出现下标,如果找到字符最远出现位置下标和当前下标相等了,则找到了分割点
class Solution {
public:vector<int> partitionLabels(string s) {// 更新每个字母出现的最远下标// 区间内更新其中每个字母出现的最远下标,i等于这个下标的时候片段+1int hash[27] = {0};//hash数组里面是不同字母对应的ascll码的最远下标 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;}
};
8.加油站
在一条环路上有 n 个加油站,其中第 i 个加油站有汽油 gas[i] 升。
你有一辆油箱容量无限的的汽车,从第 i 个加油站开往第 i+1 个加油站需要消耗汽油 cost[i] 升。你从其中的一个加油站出发,开始时油箱为空。
给定两个整数数组 gas 和 cost ,如果你可以按顺序绕环路行驶一周,则返回出发时加油站的编号,否则返回 -1 。如果存在解,则 保证 它是 唯一 的。
直接从全局进行贪心选择,情况如下:
- 情况一:如果gas的总和小于cost总和,那么无论从哪里出发,一定是跑不了一圈的
- 情况二:rest[i] = gas[i]-cost[i]为一天剩下的油,i从0开始计算累加到最后一站,如果累加没有出现负数,说明从0出发,油就没有断过,那么0就是起点。
- 情况三:如果累加的最小值是负数,汽车就要从非0节点出发,从后向前,看哪个节点能把这个负数填平,能把这个负数填平的节点就是出发节点。
首先遍历计算每一个加油站点留下来的rest
然后rest累加,并且同时关注这个累加的curSum是否小于0,小于0就记录给min,当遍历完之后,可以得到curSum的值,还有累加过程中最小的一个和min
如果curSum小于0,情况一
如果min >= 0,情况二
情况三,min的值就有用了,之前记录了最小的和,现在从后往前,一个个加给他,
当从后向前进行遍历时,实际上是在尝试找到一个点,从该点出发(不论是向前还是向后)都能保证剩余油量始终大于等于零。
class Solution {
public:int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {int curSum = 0;int min = 0;for(int i = 0; i < gas.size(); i ++){int rest = gas[i] - cost[i];curSum += rest;if(curSum < min){min = curSum;}}if(curSum < 0) return -1;if(min >= 0) return 0;for(int i = gas.size() - 1; i >= 0; i --){int rest = gas[i] - cost[i];min += rest;if(min >= 0) return i;}return -1;}
};