算法学习——贪心算法

目录

一,柠檬水找零

1.题目

2.题目接口

3.题目解题思路

二,将数组减半的最小操作数

 1.题目

2.题目接口

3.解题思路及代码

三,摆动序列

1.题目

2.题目接口

3.解题代码及思路


一,柠檬水找零

1.题目

在柠檬水摊上,每一杯柠檬水的售价为 5 美元。顾客排队购买你的产品,(按账单 bills 支付的顺序)一次购买一杯。

每位顾客只买一杯柠檬水,然后向你付 5 美元、10 美元或 20 美元。你必须给每个顾客正确找零,也就是说净交易是每位顾客向你支付 5 美元。

注意,一开始你手头没有任何零钱。

给你一个整数数组 bills ,其中 bills[i] 是第 i 位顾客付的账。如果你能给每位顾客正确找零,返回 true ,否则返回 false 。

2.题目接口

class Solution {
public:
    bool lemonadeChange(vector<int>& bills) {

    }
};

3.题目解题思路

首先来看解题代码:

class Solution {
public:
    bool lemonadeChange(vector<int>& bills) {

        int five = 0;
        int ten = 0;

        for(int i = 0;i<bills.size();i++)
        {
           if(bills[i] == 5)
           {
               five++;
           }

           else if(bills[i] == 10)
           {
               if(five == 0)
               {
                   return false;
               }

               five--;
               ten++;
           }

           else
           {
              if(ten>0)
              {
                 if(five ==0)
                 {
                    return false;
                 }
                 ten--;
                 five--;
              }

              else
              {
                  if(five<3)
                  {
                      return false;
                  }

                  five-=3;
              }
           }
        }

         return true;
    }
};

这道题的解题思路我是这样想的。因为我们的钱数只有三种:5,10,20。首先,20是肯定不能拿来找零的。所以我们便定义两个变量five和ten来表示能拿来找零的five和ten的数量。之后我们便来分类讨论:

1.如果账单中是五美元便直接将five++。这时不用找零。

2.如果账单中出现了十美元这时便要找零,这是就得先检查five的数量是否大于0。若five的数量等于0那就直接返回false。如不是便将five--,ten++。

3.第三种情况便是出现了二十美元,这个时候找零便有两种情况:

1.找一张ten+一张five。

2.找三张five。

这两种情况中应该将第一种情况作为优先选项。倘若两者都不成立便返回false。

循环结束还没有返回便可以返回true,找零成功!!!

二,将数组减半的最小操作数

 1.题目

给你一个正整数数组 nums 。每一次操作中,你可以从 nums 中选择 任意 一个数并将它减小到 恰好 一半。(注意,在后续操作中你可以对减半过的数继续执行操作)

请你返回将 nums 数组和 至少 减少一半的 最少 操作数。

2.题目接口

class Solution {
public:
    int halveArray(vector<int>& nums) {
        
    }
};

3.解题思路及代码

先来看解题代码:

class Solution {
public:
    int halveArray(vector<int>& nums) {
        double half = 0;
        double total = 0;
        int count = 0;
        priority_queue<double> heap;

        for(int i = 0;i<nums.size();i++)
        {
           heap.push(nums[i]);
           total+=nums[i];
        }

        total = total/2;
        cout<<heap.top()<<endl;


        for(int i = 0;i<nums.size();i++)
        {
            double top = heap.top();
            heap.pop();
            half+= top/2;
            count++;
            heap.push(top/2);

            if(half>=total)
            {
                break;
            }

        }

        return count;
    };
};

要解决这个问题,我们用的思路便是找到这个数组中的最大值然后将这个最大值砍半。那我们该怎么找呢?这里便要用到一个数据结构叫做优先级队列,优先级队列的top()便是它的最大值。解决步骤如下:

1.将nums数组里的数据push到优先级队列中。并求出nums中元素的和total。

2.将total砍半,利用优先级队列将最大值top求出,并将top砍半,用half记录。并将操作数count++。

3.将heap原来的top删除掉并将砍半后的top插入到heap中。

4.如果half>=total便可以break,然后返回count。

三,摆动序列

1.题目

如果连续数字之间的差严格地在正数和负数之间交替,则数字序列称为 摆动序列 。第一个差(如果存在的话)可能是正数或负数。仅有一个元素或者含两个不等元素的序列也视作摆动序列。

  • 例如, [1, 7, 4, 9, 2, 5] 是一个 摆动序列 ,因为差值 (6, -3, 5, -7, 3) 是正负交替出现的。

  • 相反,[1, 4, 7, 2, 5] 和 [1, 7, 4, 5, 5] 不是摆动序列,第一个序列是因为它的前两个差值都是正数,第二个序列是因为它的最后一个差值为零。

子序列 可以通过从原始序列中删除一些(也可以不删除)元素来获得,剩下的元素保持其原始顺序。

给你一个整数数组 nums ,返回 nums 中作为 摆动序列 的 最长子序列的长度 。

2.题目接口

class Solution {
public:
    int wiggleMaxLength(vector<int>& nums) {

    }
};

3.解题代码及思路

解题代码:

class Solution {
public:
    int wiggleMaxLength(vector<int>& nums) {
          int count = 0;
          int left = 0,right = 0;
          
          if(nums.size()<2)
          {
              return 1;
          }
          

          for(int i = 0;i<nums.size()-1;i++)
          {
             int right = nums[i+1]-nums[i];

             if(right == 0)//遇到相同大小的数据便跳过。
             {
                 continue;
             }

             if(right*left<=0)//当找到波峰或者波谷时便统计。
             {
                 count++;
             }

             left = right;//交换两边的单调性。
          }

         

          return count+1;//加上不能统计到的最右边的数。
    }
};

解题思路:

这道题的最佳解题思路其实就是找到这个数组的折线图的波峰与波谷,然后将这些波峰和波谷的数量统计下来便得到了最优解。

那我们该如何统计波峰和波谷呢?首先我们要知道的是在折线统计图中,波峰和波谷的两边的单调性是不一样的,所以可以利用这个性质来统计波峰和波谷。

步骤:

1.设计两个值:left,right。这两个值分别来统计一个值两边的单调性。

2.当这两个值的乘积为负数或者0时便将cout++。为什么乘积为0时还可以加加呢?

这是因为存在这样两种情况:

1.数组的数据为[1,2,2,2,2,1]时,折线图为:

这是我们的摆动序列便是:[1,2,1]。在这里指挥计算最后一个2因为这条语句:

 if(right == 0) continue;
             

对于最后一个2来说,right*left == 0。

还有一种情况,比如当数组的数据为:[2,1,1,1,1,2]时。折线图如下:

摆动序列便是[2,1,2]。也是只会统计最后一个1,它的right*left == 0。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值