(1)455分发饼干–简单
假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。
对每个孩子 i,都有一个胃口值 g[i],这是能让孩子们满足胃口的饼干的最小尺寸;并且每块饼干 j,都有一个尺寸 s[j] 。如果 s[j] >= g[i],我们可以将这个饼干 j 分配给孩子 i ,这个孩子会得到满足。你的目标是尽可能满足越多数量的孩子,并输出这个最大数值。
class Solution {
public:
int findContentChildren(vector<int>& g, vector<int>& s) {
//升序排序
sort(g.begin(), g.end());
sort(s.begin(), s.end());
int cookie = 0;
int child = 0;
//贪心算法,尽可能用小饼干满足更多孩子
while(cookie < s.size() && child < g.size()){
if(g[child] <= s[cookie]){
//该孩子满足了,满足下一个孩子
child++;
}
//如果未满足,则当前饼干一定不能满足剩下的孩子,移动到下一块
//如果满足,则当前饼干被消耗,移动到下一块
cookie++;
}
return child;
}
};
(2)376摆动序列–中等
如果连续数字之间的差严格地在正数和负数之间交替,则数字序列称为 摆动序列 。第一个差(如果存在的话)可能是正数或负数。仅有一个元素或者含两个不等元素的序列也视作摆动序列。
例如, [1, 7, 4, 9, 2, 5] 是一个 摆动序列 ,因为差值 (6, -3, 5, -7, 3) 是正负交替出现的。
相反,[1, 4, 7, 2, 5] 和 [1, 7, 4, 5, 5] 不是摆动序列,第一个序列是因为它的前两个差值都是正数,第二个序列是因为它的最后一个差值为零。
子序列 可以通过从原始序列中删除一些(也可以不删除)元素来获得,剩下的元素保持其原始顺序。
给你一个整数数组 nums ,返回 nums 中作为 摆动序列 的 最长子序列的长度 。
连续递增或递减时,选择红色点
class Solution {
public:
int wiggleMaxLength(vector<int>& nums) {
if(nums.size()==0) return 0;
if(nums.size()==1) return 1;
//利用状态机转换
//三个状态
static const int begin = 0;
static const int up = 1;
static const int down = 2;
int STATE = begin;
int maxLength = 1;//初始长度为1
for(int i = 1; i< nums.size(); i++){
switch(STATE){
//当状态为begin时
case begin:
if(nums[i]>nums[i-1]){
STATE = up;
maxLength++;
}
else if(nums[i] < nums[i-1]){
STATE = down;
maxLength++;
}
//如果两数相等,不发生状态转换
break;
case up:
if(nums[i] < nums[i-1]){
STATE = down;
maxLength ++;
}
//如果两数相等或后一个大于前一个,不发生状态转换
break;
case down:
if(nums[i] > nums[i-1]){
STATE = up;
maxLength ++;
}
//如果两数相等或后一个小于前一个,不发生状态转换
break;
}
}return maxLength;
}
};
(3)402移调k位数–中等
给你一个以字符串表示的非负整数 num 和一个整数 k ,移除这个数中的 k 位数字,使得剩下的数字最小。请你以字符串形式返回这个最小的数字。
#include<string>
using namespace std;
class Solution {
public:
string removeKdigits(string num, int k) {
if(k >= num.size()) return "0";
vector<int> S;
string result = "";
int number;
for(int i = 0; i< num.length(); i++){
//从高位开始,保留尽可能小的数
number = num[i] - '0';//字符串转换为数字,0要用单引号,表示一个字符,双引号表示一个字符串
while(S.size()> 0 && number < S[S.size() - 1] && k>0){
//if(S.size() == 1 && number == 0) break;//如果栈中只有一个元素,且新来的元素是0,不能用0替换栈顶
S.pop_back();//如果新元素比栈顶小,就保留新元素
k--;
}
//0不能作为开头元素
if(number != 0 || S.size() != 0 ){
S.push_back(number);
}
}
if(S.size() == 0 || k >= S.size()) return "0";
//如果序列都遍历完而k还未用完时,得到的栈中元素一定是单调递增的。
while(k > 0) {
S.pop_back();
k--;
}
if(S.size() == 0) return "0";
for(int i = 0; i< S.size(); i++){
//char tmp = S[i]+'0';
//result = result+ tmp;//这样转字符,超长用例会报超时,不知道为啥
result.append(1,S[i]+'0');//在当前字符串结尾添加1个字符
}
return result;
}
};
append函数的用法参考:https://blog.csdn.net/weixin_42258743/article/details/107964192
(4)55跳跃游戏
给你一个非负整数数组 nums ,你最初位于数组的 第一个下标 。数组中的每个元素代表你在该位置可以跳跃的最大长度。
判断你是否能够到达最后一个下标,如果可以,返回 true ;否则,返回 false 。
法一:
class Solution {
public:
bool canJump(vector<int>& nums) {
//关键在于如何选择下一条的节点
//下一跳能跳的越远越有利,因此选择能达到最远的节点
if(nums.size()== 1) return true;
int next_index = -1;
int index = 0;
int max_distance = 0;//此处赋值为负数的话会导致与size函数返回值(无符号数)比较出错
while((index+max_distance <= (nums.size()-1)) && (next_index != index)){
//下一跳等于当前下标,则证明不能到达
//下一条等于大于总长度,则证明可到达
max_distance = 0;
//index更新至下一跳处
if (next_index != -1) index = next_index;
//cout<<"index = "<< index << " next_index = "<<next_index<<endl;
if(nums[index] == 0) break;
else{
for(int i = 1; i<= nums[index] && (i+index<nums.size()); i++){
//遍历从index到index+nums[i]能到达的所有元素,找出最远距离
if((i+nums[i+index]) > max_distance){
max_distance = (i+nums[i+index]);
next_index = index+i;
//cout<< " i="<<i<<" max_distance=" << max_distance<<endl;
}
}
}
//cout<<"index = "<< index << " next_index = "<<next_index<<endl;
}
if((index+max_distance >= (nums.size()-1)) ) return true;
return false;
}
};
法二:
class Solution {
public:
bool canJump(vector<int>& nums) {
if(nums.size() == 1) return true;
int length = nums.size();//总长度
int tmp = nums[0];//tmp记录当前能到达的最远距离
int i = 1;
for(i; (i< length && tmp > 0); i++){
tmp--;
cout<< "tmp="<<tmp<<" nums[" << i<<"]=" << nums[i]<< " ";
//如果当前节点能到达的距离比之前的远,tmp更新为当前节点的值
if(nums[i] > tmp){
tmp = nums[i];
}
//cout<< tmp << "picked"<<endl;
}
//cout<< "tmp="<<tmp<<endl;
if(i >= length && tmp >= 0) return true;
return false;
}
};
(5)45跳跃游戏二–中等
给定一个长度为 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]。
此题根据跳跃游戏的 法一 改编得到
https://blog.csdn.net/weixin_44343355/article/details/128772719
class Solution {
public:
int jump(vector<int>& nums) {
//关键在于如何选择下一条的节点
//下一跳能跳的越远越有利,因此选择能达到最远的节点
if(nums.size() < 2) return 0;
int next_index = 0;
int index = 0;
int max_distance = nums[0];//此处赋值为负数的话会导致与size函数返回值(无符号数)比较出错
int jump_times = 0;
int length = nums.size();
while(((max_distance) < length -1)){
//最远能到的距离等于大于总长度,则证明可到达
for(int i = 1; i<= nums[index] && (i+index<nums.size()); i++){
//遍历从index到index+nums[i]能到达的所有元素,找出最远距离
if((i+nums[i+index]+index) > max_distance){
max_distance = (i+nums[i+index]+index);
next_index = index+i;
//cout<< " i="<<i<<" max_distance=" << max_distance<<endl;
}
}
index = next_index;
jump_times++;
}
return jump_times+1;
}
};
(6)452用最少数量的箭引爆气球–中等
有一些球形气球贴在一堵用 XY 平面表示的墙面上。墙面上的气球记录在整数数组 points ,其中points[i] = [xstart, xend] 表示水平直径在 xstart 和 xend之间的气球。你不知道气球的确切 y 坐标。
一支弓箭可以沿着 x 轴从不同点 完全垂直 地射出。在坐标 x 处射出一支箭,若有一个气球的直径的开始和结束坐标为 xstart,xend, 且满足 xstart ≤ x ≤ xend,则该气球会被 引爆 。可以射出的弓箭的数量 没有限制 。 弓箭一旦被射出之后,可以无限地前进。
给你一个数组 points ,返回引爆所有气球所必须射出的 最小 弓箭数 。
//注意,此处需要加引用
bool cmp(const vector<int> &a, const vector<int> &b){
return a[0] < b[0];
}
class Solution {
public:
int findMinArrowShots(vector<vector<int>>& points) {
if(points.size() == 1) return 1;
sort(points.begin(), points.end(), cmp);//按左端点从小到大升序排序
int length = points.size();
int shootNum = 1;
int shooter_begin = points[0][0];
int shooter_end = points[0][1];
for(int i = 1;i<length;i++){
//如果新节点的左端点在shooter的范围内,则说明两区间有重合部分,需要一箭即可射穿
if(points[i][0] >= shooter_begin && points[i][0] <= shooter_end){
//更新shooter的左端点
shooter_begin = points[i][0];
//如果shooter的右端点大于新节点的右端点,则更新右端点
if(points[i][1] <= shooter_end){
shooter_end = points[i][1];
}
}
//如果无交集,则新选择一个射手
else{
shootNum++;
shooter_begin = points[i][0];
shooter_end = points[i][1];
}
}
return shootNum;
}
};
(7)871最低加油次数–困难
汽车从起点出发驶向目的地,该目的地位于出发位置东面 target 英里处。
沿途有加油站,用数组 stations 表示。其中 stations[i] = [positioni, fueli] 表示第 i 个加油站位于出发位置东面 positioni 英里处,并且有 fueli 升汽油。
假设汽车油箱的容量是无限的,其中最初有 startFuel 升燃料。它每行驶 1 英里就会用掉 1 升汽油。当汽车到达加油站时,它可能停下来加油,将所有汽油从加油站转移到汽车中。
为了到达目的地,汽车所必要的最低加油次数是多少?如果无法到达目的地,则返回 -1 。
注意:如果汽车到达加油站时剩余燃料为 0,它仍然可以在那里加油。如果汽车到达目的地时剩余燃料为 0,仍然认为它已经到达目的地。
此题的思路与跳跃游戏二中的思路类似,都是求最小跳跃次数,用贪心的思想
class Solution {
public:
int minRefuelStops(int target, int startFuel, vector<vector<int>>& stations) {
if(stations.size() == 0){
if(target > startFuel) return -1;
else return 0;
}
// if(stations.size() == 1){
// if(target < startFuel) return 0;
// else if(stations[0][0] > startFuel || (stations[0][1]+startFuel) < target) return -1;
// else return 1;
// }
int leftFuel = startFuel;//剩余油量
priority_queue<int, vector<int>, less<int>> maxFuel;//途中站点的最大油量,加过油的站点就不能再加油了,所以用 堆来存储,依次输出最大站点
int addFuelTimes = 0;//加油次数
int i = 0;
int maxfuel = 0;
vector<int> target_vector;
target_vector.push_back(target);
target_vector.push_back(0);
stations.push_back(target_vector);
for(i = 0; i< stations.size(); i++){
//cout<< "i="<<i<<" ";
leftFuel = startFuel - stations[i][0];//剩余油量
//剩余油量小于等于0,则表明过程中需要加一次油
//无法到达该点,先加油
while(leftFuel < 0 && maxFuel.size()>0){
maxfuel = maxFuel.top();
maxFuel.pop();
leftFuel = leftFuel + maxfuel;
startFuel = startFuel +maxfuel;
addFuelTimes ++;
}
if(leftFuel<0) return -1;
maxFuel.push(stations[i][1]);
//前面所有站点的油都加完了,如果还到不了该点,返回无法到达
//cout<<endl;
}return addFuelTimes;
}
};