【LeetCode系列】贪心算法专题

贪心算法专题

目录

贪心算法专题

LeetCode 860 柠檬水找零

1、分析

2、代码

LeetCode 392 判断子序列

1、分析

2、代码

LeetCode 455 分发饼干

1、分析

2、代码

LeetCode 55 跳跃游戏

1、分析

2、代码

LeetCode 45 跳跃游戏II

1、分析

2、代码

LeetCode 376 摆动序列

1、分析

2、代码

LeetCode 406 根据身高重建队列

1、分析

2、代码

LeetCode 452 用最少数的剑引爆气球

1、分析

2、代码

 LeetCode 402 移除k位数字

1、分析

2、代码

LeetCode 134 加油站

1、分析

2、代码


LeetCode 860 柠檬水找零

题目:https://leetcode-cn.com/problems/lemonade-change/submissions/

在柠檬水摊上,每一杯柠檬水的售价为 5 美元。

顾客排队购买你的产品,(按账单 bills 支付的顺序)一次购买一杯。

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

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

如果你能给每位顾客正确找零,返回 true ,否则返回 false 。

示例 1:

输入:[5,5,5,10,20]
输出:true
解释:
前 3 位顾客那里,我们按顺序收取 3 张 5 美元的钞票。
第 4 位顾客那里,我们收取一张 10 美元的钞票,并返还 5 美元。
第 5 位顾客那里,我们找还一张 10 美元的钞票和一张 5 美元的钞票。
由于所有客户都得到了正确的找零,所以我们输出 true。

1、分析

       当收到5元时,我们很开心,直接收着,当收到10元时,我们需要给他找一个5元,所以得看下手上有没有5元,如果没有则直接返回false,当收到20元时,若这时我们手里既有若干10元,也有若干5元,现在问题就是先用10元还是先用5元,由于5元还可以给10元找零,所以我们首选是用10元。这其实和实际中找零时一样,我们都会先用大钱找零再用小钱凑。

2、代码

class Solution {
public:
    bool lemonadeChange(vector& bills) {
        int fives=0,tens=0;
        for(auto bill:bills)
        {
            if(bill==5) fives++;
            else if(bill==10)
            {
                if(!fives) return false;
                else{
                    fives--;
                    tens++;
                }
            }
            else{
                int t=15;
                if(tens)  //有10元,则先用10元
                {
                    tens--;
                    t-=10;
                }
                while(t&&fives)
                {
                    fives--;
                    t-=5;
                }
                if(t) return false;
            }
        }
        return true;
    }
};

LeetCode 392 判断子序列

题目:https://leetcode-cn.com/problems/is-subsequence/

给定字符串 s 和 t ,判断 s 是否为 t 的子序列。

你可以认为 s 和 t 中仅包含英文小写字母。字符串 t 可能会很长(长度 ~= 500,000),而 s 是个短字符串(长度 <=100)。

字符串的一个子序列是原始字符串删除一些(也可以不删除)字符而不改变剩余字符相对位置形成的新字符串。(例如,"ace"是"abcde"的一个子序列,而"aec"不是)。
示例 1:

s = "abc", t = "ahbgdc"

返回 true.

1、分析

这个问题很简单,就是在字符串t中遍历每个字符,是否能找到字符串中s中的所有字符,若可以,则返回true,否则返回false。

2、代码

class Solution {
public:
    bool isSubsequence(string s, string t) {
        int k=0;
        for(int i=0;i

LeetCode 455 分发饼干

题目:https://leetcode-cn.com/problems/assign-cookies/

假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。对每个孩子 i ,都有一个胃口值 gi ,这是能让孩子们满足胃口的饼干的最小尺寸;并且每块饼干 j ,都有一个尺寸 sj 。如果 sj >= gi ,我们可以将这个饼干 j 分配给孩子 i ,这个孩子会得到满足。你的目标是尽可能满足越多数量的孩子,并输出这个最大数值。

注意:

你可以假设胃口值为正。
一个小朋友最多只能拥有一块饼干。

示例 1:

输入: [1,2,3], [1,1]

输出: 1

解释: 
你有三个孩子和两块小饼干,3个孩子的胃口值分别是:1,2,3。
虽然你有两块小饼干,由于他们的尺寸都是1,你只能让胃口值是1的孩子满足。
所以你应该输出1。

1、分析

我们先对小孩的胃口按照从小到大排序,则假设我们可以满足k个小孩的要求,那么一定是前k个,且满足他们的饼干数也是递增的,所以我们首先对两个数组进行排序。

2、代码

class Solution {
public:
    int findContentChildren(vector& g, vector& s) {
        sort(g.begin(),g.end());
        sort(s.begin(),s.end());
        int n=g.size();
        int res=0;
        for(int i=0,j=0;i

LeetCode 55 跳跃游戏

题目:https://leetcode-cn.com/problems/jump-game/submissions/

给定一个非负整数数组,你最初位于数组的第一个位置。

数组中的每个元素代表你在该位置可以跳跃的最大长度。

判断你是否能够到达最后一个位置。

示例 1:

输入: [2,3,1,1,4]
输出: true
解释: 我们可以先跳 1 步,从位置 0 到达 位置 1, 然后再从位置 1 跳 3 步到达最后一个位置。

1、分析

我们每次更新当前能跳到的最远距离,看最后能跳到的最远的距离是否能跳到最后一块石头。

2、代码

class Solution {
public:
    bool canJump(vector& nums) {
        if(nums.empty()) return false;
        int maxdist=0;
        for(int i=0;i=nums.size()-1) return true;  //看最后最远的距离能否跳到最后一步
        return false;
    }
};

LeetCode 45 跳跃游戏II

题目:https://leetcode-cn.com/problems/jump-game-ii/

给定一个非负整数数组,你最初位于数组的第一个位置。

数组中的每个元素代表你在该位置可以跳跃的最大长度。

你的目标是使用最少的跳跃次数到达数组的最后一个位置。

示例:

输入: [2,3,1,1,4]
输出: 2
解释: 跳到最后一个位置的最小跳跃数是 2。
从下标为 0 跳到下标为 1 的位置,跳 1 步,然后跳 3 步到达数组的最后一个位置。

说明:

假设你总是可以到达数组的最后一个位置。

1、分析

按照第一想法是直接按照每一次能走的最大步数,但是这样对于例子来说,第一次我们跳两步,我们跳到了1,再往后跳1步,又跳到了下一个1,最后跳到4,一共跳了3次,而如果我们选择第一次跳一步跳到3,然后下一步直接跳3步,就可以跳到终点了,这样只用了2次。

【LeetCode系列】贪心算法专题_第1张图片

2、代码

class Solution {
public:
    int jump(vector& nums) {
        if(nums.empty()) return 0;
        int left=0,right=0;  //我们用两个变量来维护区域的左右端点
        int res=0;
        for(int i=0;i+1

LeetCode 376 摆动序列

题目:https://leetcode-cn.com/problems/wiggle-subsequence/

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

例如, [1,7,4,9,2,5] 是一个摆动序列,因为差值 (6,-3,5,-7,3) 是正负交替出现的。相反, [1,4,7,2,5] 和 [1,7,4,5,5] 不是摆动序列,第一个序列是因为它的前两个差值都是正数,第二个序列是因为它的最后一个差值为零。

给定一个整数序列,返回作为摆动序列的最长子序列的长度。 通过从原始序列中删除一些(也可以不删除)元素来获得子序列,剩下的元素保持其原始顺序。

示例 1:

输入: [1,7,4,9,2,5]
输出: 6 
解释: 整个序列均为摆动序列。

1、分析

首先我们可以先把重复的数据删除,只剩下一个,这样就不用考虑当左右数据相同时的情况了。

【LeetCode系列】贪心算法专题_第2张图片

2、代码

class Solution {
public:
    int wiggleMaxLength(vector& nums) {
        nums.erase(unique(nums.begin(),nums.end()),nums.end()); //先是删除掉重复的数字
        if(nums.size()<2) return nums.size();
        int res=2; //先加上首尾元素
        for(int i=1;i+1b&&c>b) res++;  //加上极小值
        }
        return res;
    }
};

LeetCode 406 根据身高重建队列

题目:https://leetcode-cn.com/problems/queue-reconstruction-by-height/

假设有打乱顺序的一群人站成一个队列。 每个人由一个整数对(h, k)表示,其中h是这个人的身高,k是排在这个人前面且身高大于或等于h的人数。 编写一个算法来重建这个队列。

注意:
总人数少于1100人。

示例

输入:
[[7,0], [4,4], [7,1], [5,0], [6,1], [5,2]]

输出:
[[5,0], [7,0], [5,2], [6,1], [4,4], [7,1]]

1、分析

这题的想法特别巧妙,我们先按照身高从大到小排列,当身高一样时,就按照k值从小到大排,这样我们再对那些身高比较矮的同学进行调整它的位置,这样在插入到前面时,并不会对其他的数据造成什么影响。因为比你矮的人站你前面,你也是看不到的。

2、代码

bool cmp(vector &a,vector &b)
    {
        return a[0]>b[0]||a[0]==b[0]&&a[1]> reconstructQueue(vector>& people) {
        sort(people.begin(),people.end(),cmp);
        vector> res;
        for(auto p:people) res.insert(res.begin()+p[1],p); //把每个数据插入到它指定的位置

        return res;
    }
};

LeetCode 452 用最少数的剑引爆气球

题目:https://leetcode-cn.com/problems/minimum-number-of-arrows-to-burst-balloons/

在二维空间中有许多球形的气球。对于每个气球,提供的输入是水平方向上,气球直径的开始和结束坐标。由于它是水平的,所以y坐标并不重要,因此只要知道开始和结束的x坐标就足够了。开始坐标总是小于结束坐标。平面内最多存在104个气球。

一支弓箭可以沿着x轴从不同点完全垂直地射出。在坐标x处射出一支箭,若有一个气球的直径的开始和结束坐标为 xstart,xend, 且满足  xstart ≤ x ≤ xend,则该气球会被引爆。可以射出的弓箭的数量没有限制。 弓箭一旦被射出之后,可以无限地前进。我们想找到使得所有气球全部被引爆,所需的弓箭的最小数量。

Example:

输入:
[[10,16], [2,8], [1,6], [7,12]]

输出:
2

解释:
对于该样例,我们可以在x = 6(射爆[2,8],[1,6]两个气球)和 x = 11(射爆另外两个气球)。

1、分析

这题的思路也特别巧妙,我们按照每个气球的右端点按照从小到大进行排序。

【LeetCode系列】贪心算法专题_第3张图片

2、代码

bool cmp(vector &a,vector &b)
{
    return a[1]>& points) {
        sort(points.begin(),points.end(),cmp);
        if(points.size()<2) return points.size();
        int res=1,r=points[0][1];     //第一个引爆点肯定是在第一段的右端点的
        for(int i=1;ir) //若下一段的左端点不在当前引爆点之内,则需要更新引爆点
            {
                res++;
                r=points[i][1];
            }
        }
        return res;
    }
};

 LeetCode 402 移除k位数字

题目:https://leetcode-cn.com/problems/remove-k-digits/

给定一个以字符串表示的非负整数 num,移除这个数中的 k 位数字,使得剩下的数字最小。

注意:

num 的长度小于 10002 且 ≥ k。
num 不会包含任何前导零。

示例 1 :

输入: num = "1432219", k = 3
输出: "1219"
解释: 移除掉三个数字 4, 3, 和 2 形成一个新的最小的数字 1219。

1、分析

对于一个单调递增的数列,我们需要删除k个数,使最后剩下的数结果最小,那么我们肯定选择删除后k个数,所以当我们从第一个数开始看,当目前的数是单调递增时,我们肯定是先保留的,当有个数破坏了当前的递增性,那么我们就要把当前数删掉,前提是我们还可以删除数据。最后若还可以删除数据,则由于当前剩下的数据是递增的,所以直接删除末尾的数即可。

2、代码

class Solution {
public:
    string removeKdigits(string num, int k) {
        string res;
        for(auto c:num)
        {
            //保持剩下的数据递增
            while(res.size()&&res.back()>c&&k) //若当前数小于答案中的最后一个数,则删掉当前这个数
            {
                res.pop_back();
                k--;
            }
            res+=c;
        }
        while(k--) res.pop_back(); //若还可以删除数据,则直接删除末尾的数即可
        int i=0;
        while(i<=res.size()&&res[i]=='0') i++;  //剔除掉开头的0
        if(i==res.size()) return "0";
        else return res.substr(i);
    }
};

LeetCode 134 加油站

题目:https://leetcode-cn.com/problems/gas-station/

在一条环路上有 N 个加油站,其中第 i 个加油站有汽油 gas[i] 升。

你有一辆油箱容量无限的的汽车,从第 i 个加油站开往第 i+1 个加油站需要消耗汽油 cost[i] 升。你从其中的一个加油站出发,开始时油箱为空。

如果你可以绕环路行驶一周,则返回出发时加油站的编号,否则返回 -1。

说明: 

如果题目有解,该答案即为唯一答案。
输入数组均为非空数组,且长度相同。
输入数组中的元素均为非负数。

示例 1:

输入: 
gas  = [1,2,3,4,5]
cost = [3,4,5,1,2]

输出: 3

解释:
从 3 号加油站(索引为 3 处)出发,可获得 4 升汽油。此时油箱有 = 0 + 4 = 4 升汽油
开往 4 号加油站,此时油箱有 4 - 1 + 5 = 8 升汽油
开往 0 号加油站,此时油箱有 8 - 2 + 1 = 7 升汽油
开往 1 号加油站,此时油箱有 7 - 3 + 2 = 6 升汽油
开往 2 号加油站,此时油箱有 6 - 4 + 3 = 5 升汽油
开往 3 号加油站,你需要消耗 5 升汽油,正好足够你返回到 3 号加油站。
因此,3 可为起始索引。

1、分析

我们需要枚举每个起点,然后依次往后走,当当前油不够下一站时,则break,当有一次走完了全程时,则是成功的。

优化的关键:

【LeetCode系列】贪心算法专题_第4张图片

2、代码

class Solution {
public:
    int canCompleteCircuit(vector& gas, vector& cost) {
        int n=gas.size(),j=0;
        for(int i=0;i

 

你可能感兴趣的:(LeetCode经典题目讲解)