代码随想录算法训练营Day36|860.柠檬水找零、406.根据身高重建队列、452.用最少的箭引爆气球

柠檬水找零

860. 柠檬水找零 - 力扣(LeetCode)

        由于付账的bills数组是有顺序的,当顾客支付5美元时,我们收下5美元,当顾客支付10美元时,我们返还5美元,当顾客支付20美元时,我们可以返还一张10美元、1张5美元或者三张5美元,因此,我们需要2个变量存储拥有的5美元和10美元的数量,并根据bills数组进行变量的改变,当变量存在负值时,说明找零失败,return false。

class Solution {
public:
    bool lemonadeChange(vector<int>& bills) {
        // 初始化两个计数器,分别用于记录5美元和10美元钞票的数量。
        int five = 0;
        int ten = 0;

        // 遍历顾客支付的钞票序列。
        for (int i = 0; i < bills.size(); i++) {
            // 如果顾客支付5美元,增加5美元钞票的计数器。
            if (bills[i] == 5) {
                five++;
                // 继续处理下一个钞票,不进行额外的操作。
                continue;
            }

            // 如果顾客支付10美元,增加10美元钞票的计数器,并减少5美元钞票的计数器。
            if (bills[i] == 10 && five != 0) {
                ten++;
                five--;
                // 继续处理下一个钞票,不进行额外的操作。
                continue;
            }

            // 如果顾客支付20美元,需要提供正确的零钱。
            if (bills[i] == 20) {
                // 如果10美元钞票和5美元钞票都有,则提供10美元和5美元的零钱。
                if (ten != 0 && five != 0) {
                    ten--;
                    five--;
                    // 继续处理下一个钞票,不进行额外的操作。
                    continue;
                }
                // 如果只有5美元钞票,但数量足够(至少3张),则提供3张5美元的零钱。
                else if (ten == 0 && five >= 3) {
                    five -= 3;
                    // 继续处理下一个钞票,不进行额外的操作。
                    continue;
                }
            }
            // 如果无法提供正确的零钱,返回 false。
            return false;
        }
        // 如果能够处理所有钞票,返回 true。
        return true;
    }
};

算法的时间复杂度为O(n),遍历一次bills数组。

空间复杂度为O(1),只需维护2个计数器。

根据身高重建队列

406. 根据身高重建队列 - 力扣(LeetCode)

本题的思路直接参考的代码随想录的思路,代码随想录 (programmercarl.com),数组有两个维度,高度h和比他高的人数k,和之前分发糖果类似,这种情况下做贪心算法如果两头兼顾可能顾此失彼,因此,考虑对两者之一先进行排序,对h和k排序,如果按照k来从小到大排序,k的排列结果不正确的同时,身高h的结果也不正确,因此考虑按照身高h进行排序,让身高h从大到小排序(且若身高相同时,k较小的站在前面),这样,身高的顺序就确认了,然后以k为下标重新插入队列就完成了队列的重建,具体参考代码随想录的内容。

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);
        // 创建一个空的数组,用于存放重构后的队列。
        vector<vector<int>> ans;
        // 遍历排序后的 people 数组。
        for (int i = 0; i < people.size(); i++) {
            // 获取当前人的身高索引。
            int position = people[i][1];
            // 将当前人插入到 ans 数组中,位置是他们的索引。
            // 这样可以保持每个人在原始队列中的相对顺序。
            ans.insert(ans.begin() + position, people[i]);
        }
        // 返回重构后的队列。
        return ans;
    }
};

这个算法的时间复杂度为O(nlogn + n^2),一次sort快排O(nlogn),在ans数组中插入元素,由于vector数组的底层扩容机制,以及需要插入再拷贝,算法的总体时间复杂度为O(n^2)。

空间复杂度为O(n)。

使用链表 list,算法时间复杂度和空间复杂度同上,但用时会少些。

// 版本二,使用list(链表)
class Solution {
public:
    // 身高从大到小排(身高相同k小的站前面)
    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; // list底层是链表实现,插入效率比vector高的多
        for (int i = 0; i < people.size(); i++) {
            int position = people[i][1]; // 插入到下标为position的位置
            std::list<vector<int>>::iterator it = que.begin();
            while (position--) { // 寻找在插入位置
                it++;
            }
            que.insert(it, people[i]);
        }
        return vector<vector<int>>(que.begin(), que.end());
    }
};

用最少的箭引爆气球

本题和后面的无重叠区间有异曲同工之处,本质上就是找无重叠区间的个数,在重叠区间内的气球可以一箭引爆。

class Solution {
public:
    static bool cmp(vector<int>& a, vector<int>& b) {
        return a[0] < b[0];
    }
    int findMinArrowShots(vector<vector<int>>& points) {
        // 如果 points 数组为空,则不需要任何箭,返回 0。
        if (points.size() == 0) return 0;
        // 使用 sort 函数和 cmp 比较函数对 points 进行排序。
        // 排序的目的是将点按照结束位置从左到右排列。
        sort(points.begin(), points.end(), cmp);
        // 初始化箭的数量为 1,因为至少需要一支箭来覆盖第一个点。
        int arrowcount = 1;
        // 遍历排序后的 points 数组,从第二个点开始。
        for (int i = 1; i < points.size(); i++) {
            // 如果当前点的开始位置大于前一个点的结束位置,
            // 则需要一支新的箭来覆盖当前点。
            if (points[i][0] > points[i - 1][1]) {
                arrowcount++;
            }
            // 如果当前点的开始位置小于或等于前一个点的结束位置,
            // 则可以利用前一个点的箭来覆盖当前点。
            // 将当前点的结束位置更新为前一个点和当前点结束位置的最小值。
            else {
                points[i][1] = min(points[i - 1][1], points[i][1]);
            }
        }
        // 返回最少需要的箭的数量。
        return arrowcount;
    }
};

算法的时间复杂度为O(nlogn),遍历一次,快速排序一次。

空间复杂度为O(n),快速排序需要的空间。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值