供自己复习,一篇10题左右
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。
这里的局部最优就是大饼干喂给胃口大的,充分利用饼干尺寸喂饱一个,全局最优就是喂饱尽可能多的小孩 。
仔细阅读题目的要求,你的目标是尽可能满足越多数量的孩子,并输出这个最大数值。
所以必须是先遍历胃口(孩子),给再遍历饼干
不能颠倒!!!!
cpp
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)
cpp
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!!
cpp
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的值去更
cpp
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].
cpp
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] 后,剩下的区间没有重叠。
按照右边界排序!!!
cpp
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'
在遍历的过程中相当于是要找每一个字母的边界,如果找到之前遍历过的所有字母的最远边界,说明这个边界就是分割点了。此时前面出现过所有字母,最远也就到这个边界了。
可以分为如下两步:
- 统计每一个字符最后出现的位置
- 从头遍历字符,并更新字符的最远出现下标,如果找到字符最远出现位置下标和当前下标相等了,则找到了分割点
cpp
class Solution {
public:
vector<int> partitionLabels(string s) {
// 更新每个字母出现的最远下标
// 区间内更新其中每个字母出现的最远下标,i等于这个下标的时候片段+1
int 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的值就有用了,之前记录了最小的和,现在从后往前,一个个加给他,
当从后向前进行遍历时,实际上是在尝试找到一个点,从该点出发(不论是向前还是向后)都能保证剩余油量始终大于等于零。
cpp
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;
}
};