代码随想录Day30-贪心:力扣第860e、406m、452m、435m、763m题

860e. 柠檬水找零

题目链接
代码随想录文章讲解链接

方法一:贪心

用时:12m1s

思路
  • 时间复杂度: O ( n ) O(n) O(n)
  • 空间复杂度: O ( 1 ) O(1) O(1)
C++代码
class Solution {
public:
    bool lemonadeChange(vector<int>& bills) {
        int num5 = 0, num10 = 0;

        for (int& bill : bills) {
            if (bill == 5) ++num5;  // 5美元
            else if (bill == 10) {  // 10美元
                if (num5 == 0) return false;
                else {
                    --num5;
                    ++num10;
                }
            } else {  // 20美元
                if (num10 > 0) {  // 如果有10美元零钱
                    if (num5 == 0) return false;
                    else {
                        --num5;
                        --num10;
                    }
                } else {  // 如果没有10美元零钱
                    if (num5 < 3) return false;
                    else num5 -= 3;
                }
            }
        }
        return true;
    }
};

看完讲解的思考

无。

代码实现遇到的问题

无。


406m. 根据身高重建队列

题目链接
代码随想录文章讲解链接

方法一:贪心(从高到低考虑)

用时:40m17s

思路

将所有人按照身高从高到低排序,若身高相同,则按照 k i k_i ki从小到大排序,然后遍历每一个人,将他们插入到结果数组中的第 k i k_i ki个位置中,由于后遍历到的人身高低于先遍历到的人,即使将他们插入到已经位于数组中的人前面也不会导致错误的排序(局部最优)。

  • 时间复杂度: O ( n 2 ) O(n^2) O(n2),insert操作的时间复杂度是 O ( n ) O(n) O(n)
  • 空间复杂度: O ( log ⁡ n ) O(\log n) O(logn),不算存放结果的数组,所需空间是排序需要的空间。
C++代码
class Solution {
private:
    static bool cmp(vector<int>& a, vector<int>& b) {
        if (a[0] != b[0]) return a[0] > b[0];
        else return a[1] < b[1];
    }

public:
    vector<vector<int>> reconstructQueue(vector<vector<int>>& people) {
        vector<vector<int>> res;
        sort(people.begin(), people.end(), cmp);  // 排序
        for (int i = 0; i < people.size(); ++i) res.insert(res.begin() + people[i][1], people[i]);  // 将每个人插入到第k_i个位置中
        return res;
    }
};

方法二:贪心(从低到高考虑)

用时:7m59s

思路

将数组按照身高从低到高排序,若身高相同按照 k i k_i ki从高到低排序,排序后从前往后取出人放到结果数组中,对于第 i i i个人 ( h i , k i ) (h_i,k_i) (hi,ki),将其放置在第 k i + 1 k_i+1 ki+1个空位中。
贪心逻辑:这样放置后,第 i i i个人前面会有 k i k_i ki个空位,因为数组事先进行了排序,所以第 i i i个人前面非空位上的人肯定是矮于第 i i i个人的,不会对其造成影响,且对于留下来的 k i k_i ki个空位,接下来放置的人的身高肯定大于等于第 i i i个人,这样对于当前的人肯定满足要求(局部最优)。

  • 时间复杂度: O ( n 2 ) O(n^2) O(n2)
  • 空间复杂度: O ( log ⁡ n ) O(\log n) O(logn)
C++代码
class Solution {
private:
    static bool cmp(vector<int>& a, vector<int>& b) {
        if (a[0] != b[0]) return a[0] < b[0];
        else return a[1] > b[1];
    }

public:
    vector<vector<int>> reconstructQueue(vector<vector<int>>& people) {
        int size = people.size();
        vector<vector<int>> res(size);

        sort(people.begin(), people.end(), cmp);
        for (int i = 0; i < size; ++i) {  // 遍历每个人
            int space = people[i][1] + 1;  // 当前的人前面需要的空白位置
            for (int j = 0; j < size; ++j) {  // 遍历结果数组的每一个位置
                if (res[j].empty()) {
                    --space;
                    if (space == 0) {
                        res[j] = people[i];
                        break;
                    }
                }
            }
        }
        return res;
    }
};

看完讲解的思考

方法二的时间复杂度实际是要优于方法一,因为方法一中使用了insert操作,c++的vector底层是用静态数组实现的,在insert操作中,当vector的元素个数超过数组的容量,就会重新创建一个数组,然后将原先数组的元素赋值到新的数组中,扩容的操作会导致额外的时间复杂度。而方法二预先生成了确定大小的数组,后续只是将元素赋值给对应位置。

代码实现遇到的问题

无。


452m. 用最少数量的箭引爆气球

题目链接
代码随想录文章讲解链接

方法一:贪心

用时:16m2s

思路

按照左边界对points进行排序,用上一个气球的右边界来记录当前重叠区间的最小右边界,然后依次遍历points,如果当前气球的左边界大于最小右边界,则说明当前气球与上一个重叠区间不重叠,则需要的箭的数量加一。

  • 时间复杂度: O ( n log ⁡ n ) O(n \log n) O(nlogn)
  • 空间复杂度: O ( log ⁡ n ) O(\log n) O(logn)
C++代码
class Solution {
private:
    static bool cmp(const vector<int>& a, const vector<int>& b) {
        return a[0] < b[0];
    }

public:
    int findMinArrowShots(vector<vector<int>>& points) {
        int res = 1;
        sort(points.begin(), points.end(), cmp);
        for (int i = 1; i < points.size(); ++i) {
            if (points[i][0] > points[i - 1][1]) ++res;
            else points[i][1] = min(points[i - 1][1], points[i][1]);
        }
        return res;
    }
};

看完讲解的思考

无。

代码实现遇到的问题

无。


435m. 无重叠区间

题目链接
代码随想录文章讲解链接

方法一:贪心

用时:9m41s

思路

先根据区间的左边界进行排序,然后依次遍历各个区间,若当前区间的左边界小于上一个区间的右边界,则表示有重复,需要在这两个区间中丢弃一个,选择右边界比较大的区间丢弃。

  • 时间复杂度: O ( n log ⁡ n ) O(n \log n) O(nlogn)
  • 空间复杂度: O ( log ⁡ n ) O(\log n) O(logn)
C++代码
class Solution {
private:
    static bool cmp(const vector<int>& a, const vector<int>& b) {
        return a[0] < b[0];
    }

public:
    int eraseOverlapIntervals(vector<vector<int>>& intervals) {
        int res = 0;
        sort(intervals.begin(), intervals.end(), cmp);
        for (int i = 1; i < intervals.size(); ++i) {
            if (intervals[i][0] < intervals[i - 1][1]) {  // 如果当前区间的左边界小于上一个区间的右边界,则表示有重叠
                ++res;
                intervals[i][1] = min(intervals[i - 1][1], intervals[i][1]);  // 相当于舍弃掉右边界的值比较大的区间
            }
        }
        return res;
    }
};

看完讲解的思考

无。

代码实现遇到的问题

无。


763m. 划分字母区间

题目链接
代码随想录文章讲解链接

方法一:贪心

用时:24m24s

思路

贪心逻辑:首先记录每个字符最后出现的位置,然后遍历每个字符,不断更新区间的右边界(局部最优),当当前位置就是区间的右边界,则表示可以分割字符串。

  • 时间复杂度: O ( n ) O(n) O(n)
  • 空间复杂度: O ( n ) O(n) O(n)
C++代码
class Solution {
public:
    vector<int> partitionLabels(string s) {
        // 记录每个字符最后出现的位置
        unordered_map<char, int> hashMap;
        for (int i = 0; i < s.length(); ++i) hashMap[s[i]] = i;

        // 分割字符串
        int begin = 0;
        int end = hashMap[s[0]];
        vector<int> res;
        for (int i = 0; i < s.length(); ++i) {
            end = max(end, hashMap[s[i]]);  // 不断更新重合区间的右边界
            if (i == end || end == s.length()) {  // 如果当前位置就是重叠区间的右边界,则可以分割字符串;或者重叠区间的右边界已包括剩下全部字符,则剩余字符串无法再分割
                res.push_back(end - begin + 1);
                if (end == s.length()) break;  // 剩余字符串无法再分割,提前终止
                begin = i + 1;  // 新的子串的起始位置
            }
        }
        return res;
    }
};

看完讲解的思考

无。

代码实现遇到的问题

无。


最后的碎碎念

最近突然好多事,又要刷题,又要写论文,又要跑实验,又参加了个比赛,还有一堆局。真希望一天能有48个小时。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值