Day29| 134.加油站、135.分发糖果、860.柠檬水找零、406.根据身高重建队列

2025-08-06

134.加油站

题目链接:134.加油站
文档讲解:代码随想录
状态:看了一丝丝题解,AC

思路

本题中两个数组gascost是一个很复杂的东西,而两者作用相反,做差得到类似收益或利润的数组(这是本题的核心思想,很重要)。

chart

图片来源于 灵茶山艾府

上图折线图就是所有的证明:

  1. 如果gas总值小于cost,循环跑圈时总油量越来越少,不合实际。
  2. 如果gas总值等于cost,在循环中平衡,等价于有解。
  3. 解是什么?是折线图最低点

题解

class Solution {
public:
    int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
        int count = 0,min = INT_MAX,sum = 0;
        for(int i = 0;i<gas.size();i++)
        {
            gas[i] -= cost[i];
            sum += gas[i];
            if(sum<min)
            {
                count = i;
                min = sum;
            }
        }
        if(sum>=0)return (count+1)%gas.size();
        else return -1;    
    }
};

135.分发糖果

题目链接:135.分发糖果
文档讲解:代码随想录
状态:不会,且不理解

思路

整体思路很简单:保证比较性,每个节点与左孩子比较一遍,每个节点与右孩子比较一遍。

关键点:要保持累加性,比较方向要与遍历方向相同!(重要思想)

题解

class Solution {
public:
    int candy(vector<int>& ratings) {
        int count = 0;
        vector<int> candy(ratings.size(),1);
        for(int i = 0;i<ratings.size()-1;i++)
        {
            if(ratings[i+1]>ratings[i] && candy[i+1]<=candy[i]) candy[i+1] = candy[i]+1;
        }
        for(int i = ratings.size()-1;i>=1;i--)
        {
            if(ratings[i-1]>ratings[i] &&candy[i-1]<=candy[i])candy[i-1] = candy[i]+1;
        }
        for(auto i:candy)count+=i;
        return count;
    }
};

860.柠檬水找零

题目链接:860.柠檬水找零
文档讲解:代码随想录
状态:轻松AC

思路

很简单的题目:贪心的点在于能找大的尽量找大的,因为小的更有用

题解

class Solution {
public:
    bool lemonadeChange(vector<int>& bills) {
        int five = 0, ten = 0,twenty = 0;
        if(bills[0]!=5)return false;
        for(int i = 0;i<bills.size();i++)
        {
            if(bills[i] == 5)five++;
            else if(bills[i] == 10)
            {
                five--;
                if(five<0)return false;
                ten++;
            }
            else
            {
                if(ten>0)
                {
                    ten--;
                    five--;
                    if(five<0)return false;
                }
                else
                {
                    five -= 3;
                    if(five<0)return false;
                }
            }
        }
        return true;
    }
};

406.根据身高重建队列

题目链接:406.根据身高重建队列
文档讲解:代码随想录
状态:不会,学习了新思考方向与排序

思路

题目数据有两重,而要求结果是一个有序性问题。

思路:每个元素都要插入到属于它的位置,所以这一步so easy,问题转化为了从哪里开始遍历才能保证插进去是对的?

  • 选择给people数组排序,按k值排序(位置),插入时身高会影响位置,待定······
  • 选择按h值排序(身高),后面插入一定不会影响前面,有戏!
    • 如果相同hk不同咋办呢?小脑筋一动,还是前面不影响后面,问题解决!

chart

题解

class Solution {
public:
    static bool cmp(const vector<int>& a, const vector<int>& b)
    {
        if(a[0] == b[0]) return a[1]<b[1];
        return a[0]>b[0];
    }
    vector<vector<int>> reconstructQueue(vector<vector<int>>& people) {
        sort(people.begin(),people.end(),cmp);
        list<vector<int>> que;
        for(int i = 0;i<people.size();i++)
        {
            auto it = que.begin();
            advance(it,people[i][1]);
            //int num = people[i][1];
            //while(num--)it++;
            que.insert(it,people[i]);
        }
        return vector<vector<int>>(que.begin(),que.end());
    }
};

406题知识补充

小小一个题目蕴含着很大的知识量

sort对多维排序

sort排序写法:

//是针对迭代器,若有高要求自写cmp
sort(people.begin(),people.end(),cmp);

cmp比较函数的写法:

//当作模板记住,包括返回值、参数、大、小于号与排序
//static 函数属于类本身,不属于实例对象
static bool cmp(const vector<int>& a, const vector<int>& b)
{
    if(a[0] == b[0]) return a[1]<b[1];
    return a[0]>b[0];
}

vector与list容器

vector<int> 代表动态数组,里面的元素都是int类型。

对于406题未初始化数组频繁插入

对于普通数组,一旦定义了大小就不能改变,例如int a[10],这个数组a至多只能放10个元素,改不了的。

对于动态数组(vector),就是可以不用关心初始时候的大小,可以随意往里放数据,那么耗时的原因就在于动态数组的底层实现。

动态数组为什么可以不受初始大小的限制,可以随意push_back数据呢?

insert数据的时候,如果已经大于capicitycapicity会成倍扩容,但对外暴漏的size其实仅仅是+1

那么既然vector底层实现是普通数组,怎么扩容的?

就是重新申请一个二倍于原数组大小的数组,然后把数据都拷贝过去,并释放原数组内存。(对,就是这么原始粗暴的方法!)如下图所示:

chart

所以,本题极多次插入,也就是调用了超多次创建(扩容),拷贝操作,复杂度不变的情况下,多于操作增多,时间增加。

list<int> 代表双向链表,里面的元素都是int类型。

它支持在任意位置快速插入和删除元素(时间复杂度极低,参考链表),但不支持随机访问(即不能直接通过索引访问元素,只能通过迭代器遍历(类似cur = cur->next))。这使得它在某些场景下非常有用,尤其是在需要频繁插入和删除元素时。

常见用法:

auto it = que.begin();
advance(it, n);

//等价于advance,不支持.begin()+n操作
//int num = n;
//while(num--)it++;

//n前插入
que.insert(it,n);

所以对于本题频繁插入,使用list明显更实用。