860.柠檬水找零
有如下三种情况:
我以为还有更简单的方法,就情况三优先找给10+5体现了贪心的思想。
class Solution {
public:
bool lemonadeChange(vector<int>& bills) {
int wu=0,shi=0;
for(int i=0;i<bills.size();++i){
if(bills[i]==5)wu++;
else if(bills[i]==10){
wu--;
shi++;
}
else {
if(shi>0){//优先找给10+5
shi--;
wu--;
}
else wu-=3;//然后才是5*3
}
if(wu<0 || shi<0)return false;
}
return true;
}
};
406.根据身高重建队列
首先得进行排序,希望得到排序完之后的people是:序列里面的每个人的前面所有人都比自己高,这样的话从左到右,每个元素直接插入到位置是ki的地方,就能保证前ki个都是比自己高的了。
怎么排序能够保证这个结果呢,先排ki还是先排hi?序列里面的每个人的前面所有人都比自己高——说明最后的结果身高是从大到小的,先排hi再排ki会把身高又打乱,所以先排ki再排hi,排ki是从小到大排,所以确定顺序——先从小到大排ki,再从大到小排身高hi,可以模拟证实一下。
最终的步骤就是:
我就1、2分两个sort来写的,但是会报错DEADLYSIGNAL,解决办法之一是这两个sort用一个sort就能实现:当hi相同的时候,ki是从小到大的顺序,然后hi是从大到小的顺序。要保证ki是在hi之前排。还是不知道两个sort为什么有的例子报错。
class Solution {
public:
// static bool cmp_k(const vector<int> &a,const vector<int> &b){
// return a[1]<b[1];
// }
// static bool cmp_h(const vector<int> &a,const vector<int> &b){
// return a[0]>b[0];
// }
static bool cmp(const vector<int> &a,const vector<int> &b){
return a[1]<b[1];//先排k
if(a[1]==b[1])return a[0]>b[0]; //再排h
}
vector<vector<int>> reconstructQueue(vector<vector<int>>& people) {
//跟分糖果一样,不要两头兼顾,处理好一边再处理另一边
vector<vector<int>> queue;
// sort(people.begin(),people.end(),cmp_k);
// sort(people.begin(),people.end(),cmp_h);
sort(people.begin(),people.end(),cmp);
for(int i=0;i<people.size();++i){
queue.insert(queue.begin()+people[i][1],people[i]);
}
return queue;
}
};
vector底层是一个动态数组,底层是普通数组实现的。vector的容量capacity预先设定为1,size到了capacity之后,这个capacity就会增加到原先的两倍(或者其他倍),所以用vector做插入效率不高。考虑用list,list底层是链表实现。如果主要操作是基于索引的查找或需要频繁访问容器中的元素,vector是更好的选择。如果应用场景主要涉及在容器中间频繁插入或删除元素,而对随机访问的需求不高,list 可能是更合适的选择。不过,对于大多数情况,vector由于其数据连续存储而带来的缓存友好性和较低的内存开销通常是默认的首选。
用list是下面这样,自己要注意:list
不支持随机访问迭代器,所以不能使用 queue.begin() + people[i][1]
这种方式来计算插入位置。这种表达式是针对支持随机访问的容器。
class Solution {
public:
static bool cmp(const vector<int> &a,const vector<int> &b){
if(a[0]==b[0])return a[1]<b[1];//先排k
return a[0]>b[0]; //再排h
}
vector<vector<int>> reconstructQueue(vector<vector<int>>& people) {
//跟分糖果一样,不要两头兼顾,处理好一边再处理另一边
list<vector<int>> queue;
sort(people.begin(),people.end(),cmp);
for(int i=0;i<people.size();++i){
list<vector<int>>::iterator it=queue.begin();
int pos=people[i][1];
while(pos--){
it++;
}
queue.insert(it,people[i]);//插入位置不能用begin()+下标
}
return vector<vector<int>>(queue.begin(),queue.end());//转换
}
};
Type1 obj(Type2.begin(), Type2.end());
//创造一个有类型2所有元素的类型1的对象;
//常见的不同容器类型转换的方法
452. 用最少数量的箭引爆气球
有重复区间的就可以一根箭设这个重复区间,全部射穿。但是第一次做,有一个误区:先把气球的左边xstart从小到大排,最前面的气球的右边界设为cur_right,遍历的时候如果xstart≤cur_right(说明与cur_right的那个区间有重叠)的时候就不用新箭,continue;否则更新cur_right,并且count++(要用新箭)。但是如果是[0,4],[1,2],[3,4],后面2个的xstart都小于cur_right(即4),会用同一支箭continue跳过,但是应该是2支箭才行。所以这个cur_right更准确的意思应该是弓箭射出的x坐标,他遇到重复区间的时候会一直更新:在下一个气球的开始位置xstart≤cur_right的时候,更新cur_right为目前的最小右边界,才能保证,上面处理到[3,4]的时候不会用之前的箭而是射出新箭,因为此时cur_right(前一个射出的)是目前遇到的最小值2,气球[3,4]管不到。总的来说,有重叠区间的所有气球,我们只会从最小的xend(也就是最后一次更新的cur_right)那里射出。这样,利用cur_right
来持续缩小箭射出的范围,以确保每根箭能射穿最多的气球,同时在遇到不重叠的气球时射出新的箭。
class Solution {
public:
static bool cmp(const vector<int> &a,const vector<int> &b){
return a[0]<b[0];
}
int findMinArrowShots(vector<vector<int>>& points) {
if (points.empty()) return 0;
sort(points.begin(),points.end(),cmp);//xstart左区间排序
int count = 1; // 至少需要一支箭
int cur_right = points[0][1]; //对应count
for (int i = 1; i < points.size(); ++i) {
if (points[i][0] <= cur_right) { // 还和之前射出的位置cur_right有重叠
cur_right = min(cur_right, points[i][1]); // 更新应该射出的位置
continue;
}
count++; // 需要一支新箭
cur_right = points[i][1]; // 更新当前的新箭应该射出的位置
}
return count;
}
};
随想录所给的思想一样,没有多设置变量cur_right,而是不断把有重叠区间的气球的右边界缩小,这些重叠区间的气球的右边界最小值(射出新箭的前一次确定)就是射出位置。
class Solution {
public:
static bool cmp(const vector<int> &a,const vector<int> &b){
return a[0]<b[0];
}
int findMinArrowShots(vector<vector<int>>& points) {
if (points.empty()) return 0;
sort(points.begin(),points.end(),cmp);//xstart左区间排序
int count = 1; // 至少需要一支箭
for (int i = 1; i < points.size(); ++i) {
if(points[i][0]>points[i-1][1]){
count++;//需要新的一支箭
}
else{
points[i][1]=min(points[i][1],points[i-1][1]);//更新右边界
}
}
return count;
}
};