455.分发饼干
思考:首先要知道,如果一个大饼干给了一个小胃口的孩子,那么下面一个大胃王就没得吃了。所以第一步要把两个数组排序,然后,如果一个饼干不能满足最小胃口的那个孩子,那么剩下的孩子也不用喂了,都吃不饱;直接拿下一块饼干。如果一个孩子可以用更小的饼干满足,就不要把更大的饼干给他,因为可以保留更大的饼干给需求更大的孩子(贪心!)
class Solution {
public:
int findContentChildren(vector<int>& g, vector<int>& s) {
sort(g.begin(),g.end());
sort(s.begin(),s.end());
int cookies =0 ; int children = 0;
//要么饼干都分配完了,要么孩子都有饼干吃了。
while(cookies < s.size() && children < g.size()){
if(s[cookies] >= g[children]){
children++;
}
cookies ++; //因为孩子的胃口和饼干已经排序,
//所以如果当前饼干能满足孩子则给他,+1(表示这个饼干也用过了)
//不能满足的话,那么肯定也不能再给下一个孩子了,
//因为这个孩子都吃不饱,下个胃口更大也没法吃了,所以也要+1
}
return children;
}
};
376.摆动序列
思考:相邻两个元素的差值是相反的,想到了函数的每一个区间,斜率k1 × k2 < 0,如果有连续几个都是相同增长趋势时候[10,13,15],那么下一个数字要减去其中的一个,根据概率可知,只有前一个区间的数越大,那么他们差值为负的概率就越大。
使用状态机使得他们之间的变换更明显。
class Solution {
public:
int wiggleMaxLength(vector<int>& nums) {
if(nums.size() <2 )
return nums.size();
static const int BEGIN = 0;
static const int UP = 1;
static const int DOWN = 2;
int STATE = BEGIN;
int max_length = 1;
for(int i = 1 ; i< nums.size();i++){
switch(STATE){
case BEGIN:
if(nums[i]>nums[i-1]){
STATE = UP;
max_length++;
}
else if(nums[i]<nums[i-1]){
STATE = DOWN;
max_length++;
}
break;
case UP:
if(nums[i]<nums[i-1]){
STATE = DOWN;
max_length++;
}
break;
case DOWN:
if(nums[i]>nums[i-1]){
STATE = UP;
max_length++;
}
break;
}
}
return max_length;
}
};
402.移掉K位数字
问题:
1 当所有数字都扫描完成后,K仍然 > 0,怎么处理?例如:num = 12345 ,k= 3。
2 当数字中 有0出现 时候,应该有怎么样的特殊处理?例如:num = 102000,k=1
3.如何将最后结果存储为字符串并返回
思考:使得移除其中的K位数字,其实满足规律,如果越接近高位的,数字越大就把他删掉,可以用栈来实现。对于这个数字_字符串的每一位,首先要把字符串给转变成数: number = num[i] - '0';对于每一位数字,其实有两个操作:1、若它比栈顶的数要小且删除的数字k还没删完且栈里面还有元素(对呀!要是你本来都是空栈还怎么删除啊!) 2 、这个number比人家小,把人家踢出去了,自己肯定要进栈啊。
还有几个问题:边缘情况的考虑,如果要进栈的数字不为0,那直接进栈没问题;若进栈的数字为0,只要看看栈是否为空,若不为空就直接加进去没问题。如果最后结果仍然""(正好删完了或者本来就是空) ,就直接返回"0"。
class Solution {
public:
string removeKdigits(string num, int k) {
vector<int> s ;
string result = "";
for(int i = 0; i < num.size();i++){
int number = num[i] - '0';
while(k >0 && s.size()!= 0&& s.back() > number){
s.pop_back();
k--;
}
if(number || !s.empty()){
s.push_back(number);
}
}
while(!s.empty()&& k > 0){
s.pop_back();
k--;
}
for( auto n : s){
result.append(1,'0'+ n);
}
if(result == ""){
result = "0";
}
return result;
}
};
55.跳跃问题
思考:贪心算法,判断是否能够到达最后一个位置,把问题从第0个位置开始考虑,0位置max_jump = 2 ,所以 0 可以最远跳到2,当然也可以选择跳一步,到达1位置,然后又发现1位置可以跳跃3步,所以可以到达2,3,4位置。
所以,第一步,把每个位置可以到达的最远位置jump_index写出来[2,4,3,4,8]
第二步,我们其实只需要考虑从每个位置出发可以到达的最远位置就可以,以及在这个区间,每个位置可以到达的最远位置。举例子就是,0位置,可以跳到最远是2,然后,0位置还可以跳到1位置,同时又发现1位置可以跳到的最远位置是4,此时最远距离就是4。以此类推,我们只要找到这个从当前位置到最远位置,这个区间能到达的更远位置,如果有,就更换能到达的最远位置。当然控制循环的条件 1 :首先你的跳跃位置jump不可能跳出数组的最远位置;2 其次在这个区间内的跳跃,位置肯定也是在区间内(区间就是它在这个位置能跳跃到的最远位置)。
class Solution {
public:
bool canJump(vector<int>& nums) {
vector<int> index ;
for(int i = 0; i<nums.size();i++){
// index[i] = i + nums[i];
index.push_back(i + nums[i]);
}
int jump = 0;
int max_index = nums[0];
while(jump < nums.size() && jump <= max_index){
if(index[jump]> max_index){
max_index = index[jump];
}
jump++;
}
if(jump == nums.size()){
return true;
}
else
return false;
}
};
45.跳跃问题II
拓展:可以把跳跃变换的路径写出来吗?
思考:同样的,如果数组为空,或者只有一个节点,那就不用跳跃,直接返回0;如果超过1个节点,把每个位置可以到达的最远位置jump_index写出来[2,4,3,4,8]。我们要尽可能少跳跃,那么从0的位置开始,最好的办法就是只跳跃一步,那么从0最远可以跳跃到2,这是一个可以跳跃的区间,因为这里告诉肯定可以跳跃到末尾的位置,所以我们只要把jump从0位置遍历到末尾位置。接下来就变成要从每一个跳跃区间内找寻一个最大可达位置,设置一个pre_max_index和current_max_index ,jump从0一直走,走到这个区间末尾,都不跳,那么你走到末尾+1的位置,肯定是发生了跳跃,不然你不可能超过这个区间,所以当jump>current_max_index ,jumpMin++,并且此时跳跃的最大位置变成了在这个区间内可达的最大位置,比如0,1,...,i -1 ,找到max(index[0],...,index[i-1]),把他标记为现在能走到的最远位置。
class Solution {
public:
int jump(vector<int>& nums) {
if(nums.size()<2)
return 0;
vector<int> max_index ;
for(int i = 0;i<nums.size();i++){
max_index.push_back(i+nums[i]);
}
int current_max_index = nums[0];
int pre_max_index = nums[0];
int jumpMin = 1;
for(int i =1 ; i< nums.size();i++){
if( i > current_max_index){
jumpMin++;
current_max_index = pre_max_index;
}
if(pre_max_index < max_index[i] ){
pre_max_index = max_index[i];
}
}
return jumpMin;
}
};
452.用最少数量的箭引爆气球
思考:气球在平面上是横向分布的,判断用最少的箭去射穿所有的气球,实际上是贪心问题,要考虑一支箭怎么去射穿更多的气球,假设把气球都排序了,那么第一个气球的直径区域是[1,6],所以在这个区域射一箭都可以射穿,然而我们要用尽可能少的箭去射,所以若第二个气球直径区域[2,8],那么怎么用一支箭去射穿两个气球呢?肯定从[2,6]去射就可以了。
这里就有两个问题:1、什么样的气球可以一起射穿?答案是比如第i个气球,第i+1个能不能射穿?要看i+1个气球的直径起始坐标在不在i个气球的直径坐标区域之内;2、若多个气球一起射,射箭的区域变化吗?肯定变化的,[1,6],[2,8],两个气球,若一起射,那么射的区域就要从[1,6] -> [2,6],然后看后者的直径终止坐标,8,8比6大,所以只能在[2,6]射,若是第二个气球[2,5],则只能在[2,5]这个区域射箭了。依次类推,当然若气球的直径起始坐标不在区域内,就只能再多用一支箭,同时更新新的射击区域。
#inlcude <algorithm>
#inlcude <vector>
bool cmp(pair<int, int>a,pair<int, int>b){
return a.first < b.first;
}
class Solution {
public:
int findMinArrowShots(vector<pair<int, int>>& points) {
if(points.empty()){
return 0;
}
int shootNum = 1;
sort(points.begin(),points.end(),cmp);
//pair 直接调用first 而不是当做函数
int shoot_min = points[0].first;
int shoot_max = points[0].second;
for(int i =1 ;i <points.size();i++){
//如何判断能不能一箭双雕,主要要看这个气球在不在射击区间
//气球的最左边只要比射击区间最远处小 就可以射中
//这里因为做了排序 所以排除了 气球反而在射击区间左边
if(points[i].first <= shoot_max){
shoot_min = points[i].first;
if(points[i].second<shoot_max){
shoot_max = points[i].second;
}
}
else{
shootNum++;
shoot_min = points[i].first;
shoot_max = points[i].second;
}
}
return shootNum;
}
};
思考:这个题和跳跃II的问题不一样的在于,不确定能不能开到终点,并且这个不像跳跃问题,跨越了某个点就不能回头(说的不准确),比如:车在S点加油了,可以一直跑到C点,之后1KM,但是这并不妨碍A ,B两点也可以加油。解题的思路,用一个最大堆来存储沿路走过加油站的可以加油量,在某一个位置不能接续前进的时候,从最大堆中调用可以加的最大油量,看看能不能撑到下一个加油站,不能就继续加,直到能撑到下一个加油站(也就是说只要加的油能够完成当前的这段距离),如果油量的最大堆空了,说明已经不能加了,肯定到不了终点。
这里有问题其实我没想清楚?如果你传入的P= 0 /*初始油量*/,并且根据代码所写,while (!Q.empty() && P < dis) 此时可以加油的最大堆不是空么?而且P < dis(- - ! 发现不是啊!第一个站不就是<30,16>,距离为0 啊!)...所以没问题啊。直接进行下面的循环P -= dis;L = 30;Q.push(16);...好吧!
//pair 调用第一个数,直接first,而不是函数调用first()
bool cmp(const pair<int,int> &a ,const pair<int,int> &b){
return a.first > b.first;//到终点的距离,从大到小才是逆序。
}
int get_min_refule(int L, int P ,vector<pair<int,int>> &stop){
priority_queue<int> Q;
int refule_times = 0;
sort(stop.begin(),stop.end(),cmp);
for(size_t i = 0; i<stop.size();i++){
int dis = L - stop[i].first;
while(!Q.empty() && P < dis){
P += Q.top();
Q.pop();
refule_times++;
}
if(Q.empty()&&P < dis){
return -1;
}
P -= dis;
L = stop[i].first;
Q.push(stop[i].second);
}
}