力扣题解 贪心算法(2)

1005.K次取反后最大化的数组和

总结:让绝对值大的负数变为正数,当前数值达到最大,整体最优:整个数组和达到最大。

可以重复一个数一直正负,变换。

  • 第一步:将数组按照绝对值大小从大到小排序,注意要按照绝对值的大小
  • 第二步:从前向后遍历,遇到负数将其变为正数,同时K–
  • 第三步:如果K还大于0,那么反复转变数值最小的元素,将K用完
  • 第四步:求和
class Solution {
static bool cmp(int a, int b) {
    return abs(a) > abs(b);
}
public:
    int largestSumAfterKNegations(vector<int>& A, int K) {
        sort(A.begin(), A.end(), cmp);       // 第一步
        for (int i = 0; i < A.size(); i++) { // 第二步
            if (A[i] < 0 && K > 0) {
                A[i] *= -1;
                K--;
            }
        }
        if (K % 2 == 1) A[A.size() - 1] *= -1; // 第三步
        int result = 0;
        for (int a : A) result += a;        // 第四步
        return result;
    }
};

134. 加油站

总结:

for循环适合模拟从头到尾的遍历,而while循环适合模拟环形遍历,要善于使用while!

1、是贪心 当前能见消耗,不是消耗减当前,

2、不是等于 是累加+=

   class Solution {
    public:
        int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
            int curSum = 0;
            int totalSum = 0;
            int start = 0;
            for (int i = 0; i < gas.size(); i++) {
                curSum += gas[i] - cost[i];
                totalSum += gas[i] - cost[i];
                if (curSum < 0) {   // 当前累加rest[i]和 curSum一旦小于0
                    start = i + 1;  // 起始位置更新为i+1
                    curSum = 0;     // curSum从0开始
                }
            }
            if (totalSum < 0) return -1; // 说明怎么走都不可能跑一圈了
            return start;
        }
    };

135. 分发糖果

总结:

  • 一次是从左到右遍历,只比较右边孩子评分比左边大的情况。
  • 一次是从右到左遍历,只比较左边孩子评分比右边大的情况。
  class Solution {
  public:
      int candy(vector<int>& ratings) {
          vector<int> candyVec(ratings.size(), 1);
          // 从前向后
          for (int i = 1; i < ratings.size(); i++) {
              if (ratings[i] > ratings[i - 1]) candyVec[i] = candyVec[i - 1] + 1;
          }
          // 从后向前
          for (int i = ratings.size() - 2; i >= 0; i--) {
              if (ratings[i] > ratings[i + 1] ) {
                  candyVec[i] = max(candyVec[i], candyVec[i + 1] + 1);
              }
          }
          // 统计结果
          int result = 0;
          for (int i = 0; i < candyVec.size(); i++) result += candyVec[i];
          return result;
      }
  };

860.柠檬水找零

总结:

咋眼一看好像很复杂,分析清楚之后,会发现逻辑其实非常固定。

这道题目可以告诉大家,遇到感觉没有思路的题目,可以静下心来把能遇到的情况分析一下,只要分析到具体情况了,一下子就豁然开朗了。

如果一直陷入想从整体上寻找找零方案,就会把自己陷进去,各种情况一交叉,只会越想越复杂了。


```cpp
  class Solution {
    public:
        bool lemonadeChange(vector<int>& bills) {
            int five = 0, ten = 0, twenty = 0;
            for (int bill : bills) {
                // 情况一
                if (bill == 5) five++;
                // 情况二
                if (bill == 10) {
                    if (five <= 0) return false;
                    ten++;
                    five--;
                }
                // 情况三
                if (bill == 20) {
                    // 优先消耗10美元,因为5美元的找零用处更大,能多留着就多留着
                    if (five > 0 && ten > 0) {
                        five--;
                        ten--;
                        twenty++; // 其实这行代码可以删了,因为记录20已经没有意义了,不会用20来找零
                    } else if (five >= 3) {
                        five -= 3;
                        twenty++; // 同理,这行代码也可以删了
                    } else return false;
                }
            }
            return true;
        }
    };

406.根据身高重建队列

总结:

遇到两个维度权衡的时候,一定要先确定一个维度,再确定另一个维度。

此时我们可以确定一个维度了,就是身高,前面的节点一定都比本节点高!

优先按身高高的people的k来插入。插入操作过后的people满足队列属性

但使用vector是非常费时的,C++中vector(可以理解是一个动态数组,底层是普通数组实现的)如果插入元素大于预先普通数组大小,vector底部会有一个扩容的操作,即申请两倍于原先普通数组的大小,然后把数据拷贝到另一个更大的数组上。

所以使用vector(动态数组)来insert,是费时的,插入再拷贝的话,单纯一个插入的操作就是O(n (2)了,甚至可能拷贝好几次,就不止O(n)2)了。

改成链表之后,C++代码如下

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] ? a[0] > b[0] : a[1] < b[1];
        }
        vector<vector<int>> reconstructQueue(vector<vector<int>>& people) {
            list<vector<int>> ans;  // list底层使用链表实现,效率更高
            // 按照 h 降序, 按照k升序
            sort(people.begin(), people.end(), cmp);
            int n = people.size(), k;
            for (int i = 0; i < people.size(); i++) {
                auto it = ans.begin();
                k = people[i][1];
                while (k--) {
                    it++;   // 从最开始找应该插入的位置
                }
                ans.insert(it, people[i]);  // 在该位置前插入元素
            }
            return vector<vector<int>>(ans.begin(), ans.end());
        }
    };
  • 时间复杂度:O(nlog n + n^2)
  • 空间复杂度:O(n)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Quartz是OpenSymphony开源组织在Job scheduling领域又一个开源项目,它可以与J2EE与J2SE应用程序相结合也可以单独使用。Quartz可以用来创建简单或为运行十个,百个,甚至是好几万个Jobs这样复杂的程序。Jobs可以做成标准的Java组件或 EJBs。 Quartz的优势: 1、Quartz是一个任务调度框架(库),它几乎可以集成到任何应用系统中。 2、Quartz是非常灵活的,它让您能够以最“自然”的方式来编写您的项目的代码,实现您所期望的行为 3、Quartz是非常轻量级的,只需要非常少的配置 —— 它实际上可以被跳出框架来使用,如果你的需求是一些相对基本的简单的需求的话。 4、Quartz具有容错机制,并且可以在重启服务的时候持久化(”记忆”)你的定时任务,你的任务也不会丢失。 5、可以通过Quartz,封装成自己的分布式任务调度,实现强大的功能,成为自己的产品。6、有很多的互联网公司也都在使用Quartz。比如美团 Spring是一个很优秀的框架,它无缝的集成了Quartz,简单方便的让企业级应用更好的使用Quartz进行任务的调度。   课程说明:在我们的日常开发中,各种大型系统的开发少不了任务调度,简单的单机任务调度已经满足不了我们的系统需求,复杂的任务会让程序猿头疼, 所以急需一套专门的框架帮助我们去管理定时任务,并且可以在多台机器去执行我们的任务,还要可以管理我们的分布式定时任务。本课程从Quartz框架讲起,由浅到深,从使用到结构分析,再到源码分析,深入解析Quartz、Spring+Quartz,并且会讲解相关原理, 让大家充分的理解这个框架和框架的设计思想。由于互联网的复杂性,为了满足我们特定的需求,需要对Spring+Quartz进行二次开发,整个二次开发过程都会进行讲解。Spring被用在了越来越多的项目中, Quartz也被公认为是比较好用的定时器设置工具,学完这个课程后,不仅仅可以熟练掌握分布式定时任务,还可以深入理解大型框架的设计思想。
[入门数据分析的第一堂课]这是一门为数据分析小白量身打造的课程,你从网络或者公众号收集到很多关于数据分析的知识,但是它们零散不成体系,所以第一堂课首要目标是为你介绍:Ø  什么是数据分析-知其然才知其所以然Ø  为什么要学数据分析-有目标才有动力Ø  数据分析的学习路线-有方向走得更快Ø  数据分析的模型-分析之道,快速形成分析思路Ø  应用案例及场景-分析之术,掌握分析方法[哪些同学适合学习这门课程]想要转行做数据分析师的,零基础亦可工作中需要数据分析技能的,例如运营、产品等对数据分析感兴趣,想要更多了解的[你的收获]n  会为你介绍数据分析的基本情况,为你展现数据分析的全貌。让你清楚知道自己该如何在数据分析地图上行走n  会为你介绍数据分析的分析方法和模型。这部分是讲数据分析的道,只有学会底层逻辑,能够在面对问题时有自己的想法,才能够下一步采取行动n  会为你介绍数据分析的数据处理和常用分析方法。这篇是讲数据分析的术,先有道,后而用术来实现你的想法,得出最终的结论。n  会为你介绍数据分析的应用。学到这里,你对数据分析已经有了初步的认识,并通过一些案例为你展现真实的应用。[专享增值服务]1:一对一答疑         关于课程问题可以通过微信直接询问老师,获得老师的一对一答疑2:转行问题解答         在转行的过程中的相关问题都可以询问老师,可获得一对一咨询机会3:打包资料分享         15本数据分析相关的电子书,一次获得终身学习
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

RL-UAV

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值