• LeetCode之用C++写贪心算法


    Leetcode #455 分发饼干

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

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

    具体描述请查看Leetcode相关网页:https://leetcode-cn.com/problems/assign-cookies/

    方法:贪心

    贪心规律

    1.某个饼干如果不能满足某个孩子,则该饼干也一定不能满足需求因子更大的孩子
    2.某个孩子可以用更小的饼干满足,则没必要用更大饼干满足,因为可以保留更大的饼干
    3.孩子的需求因子更小则其更容易被满足,故优先从需求因子小的孩子尝试
    4.用某个饼干,满足一个较大需求因子的孩子,或满足一个较小需求因子的孩子,效果是一样的(最终满足的总量不变)

    代码如下:

    
    int findContentChildren(vector<int>& g, vector<int>& s) {
            int ans = 0;
            //对孩子的胃口和饼干大小按升序排序,然后依据贪心规律进行模拟
            sort(g.begin(), g.end());
            sort(s.begin(), s.end());
            int p = 0, q = 0;
            while(q < s.size()){
                if(p >= g.size())   break;//p >= g.size() 表示孩子都被满足了但饼干还有的多,此时要退出循环了
                if(s[q] >= g[p]){
                    ans++;
                    p++;
                }
                q++;
            }
            return ans;
        }
    

    Leetcode #435 无重叠区间

    题名:无重叠区间
    描述:
    给定一个区间的集合,找到需要移除区间的最小数量,使剩余区间互不重叠。

    说明:
    可以认为区间的终点总是大于它的起点。
    区间 [1,2] 和 [2,3] 的边界相互“接触”,但没有相互重叠。

    
    输入: [ [1,2], [2,3], [3,4], [1,3] ]
    
    输出: 1
    
    解释: 移除 [1,3] 后,剩下的区间没有重叠。
    

    具体描述请查看Leetcode相关网页:https://leetcode-cn.com/problems/non-overlapping-intervals/

    方法:贪心

    正确的思路其实很简单,可以分为以下三步:

    1.在区间集合 intervals 中选择一个区间 x,这个 x 是在当前所有区间中结束最早的(区间右边界最小),因此可以对 intervals 按照单个区间的右边界从小到大排序,例如 [ [1,2], [2,3], [3,4], [1,3] ],经过排序后成为 [ [1,2], [2,3], [1,3], [3,4] ]
    2.把所有与 x 区间相交的区间视为有重叠的区间,那么怎么判断相交呢?可以通过区间的左边界,如果后一个区间的左边界值小于前一个区间的右边界值,则视为相交;用
    noOverlapCount 用于记录不重叠区间的数量 。
    3.重复步骤 1 和 2,直到将 intervals 遍历完为止。然后用用 intervals 中的区间总数减去 noOverlapCount 即是答案;

    代码如下:

    
    static bool cmp(vector<int> a, vector<int> b){
            return a[1] < b[1];
        }
        int eraseOverlapIntervals(vector<vector<int>>& intervals) {
            if(!intervals.size())   return 0;//如果intervals为空啥都不干,返回0即可
            sort(intervals.begin(), intervals.end(), cmp);//步骤1
            int noOverlapCount = 1, index = 0, p = 1;
            while(index + p < intervals.size()){
                if(intervals[index + p][0] >= intervals[index][1]){//步骤2
                    noOverlapCount++;
                    index += p;
                    p = 1;
                }  
                else    p++;
            }
            return intervals.size() - noOverlapCount;//步骤3
        }
    

    Leetcode #452 用最少数量的箭引爆气球

    题名:用最少数量的箭引爆气球
    描述:
    在二维空间中有许多球形的气球。对于每个气球,提供的输入是水平方向上,气球直径的开始和结束坐标。由于它是水平的,所以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(射爆另外两个气球)。
    

    具体描述请查看Leetcode相关网页:https://leetcode-cn.com/problems/minimum-number-of-arrows-to-burst-balloons/

    方法:贪心

    这一题与上面一题 Leetcode #435 无重叠区间 类似,直接上代码:

    
        static bool cmp(vector<int> a, vector<int> b){
            return a[1] < b[1];
        }
        int findMinArrowShots(vector<vector<int>>& points) {
            if(!points.size())  return 0;
            int ans = 1, p = 1, anchor = 0;//anchor 用于锚定,p用于移动
            sort(points.begin(), points.end(), cmp);
            while(anchor + p < points.size()){
                if(points[anchor + p][0] <= points[anchor][1]){//将多个重叠的气球视为一个气球,只需要一支箭
                    p++;
                }
                else{
                    ans++;
                    anchor += p;
                    p = 1;
                }
            }
            return ans;
        }
    

    Leetcode #406 根据身高重建队列

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

    注意:
    总人数少于1100人。

    Example

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

    具体描述请查看Leetcode相关网页:https://leetcode-cn.com/problems/queue-reconstruction-by-height/

    方法:贪心

    对于身高比较高的人来说,比他矮的人他是‘看不见的’, 所以我们可以先对同一身高的人群按k值从小到大排序;再按身高从大到小按k值插入ans中。
    代码如下:

    
        static bool cmp(vector<int> a, vector<int> b){//排序规则
            if(a[0] != b[0])    return a[0] > b[0];
            return a[1] < b[1];
        }
        vector<vector<int>> reconstructQueue(vector<vector<int>>& people) {
            sort(people.begin(), people.end(), cmp);
            vector<vector<int> > ans;
            int index = 0;
            while(index < people.size()){
                ans.insert(ans.begin() + people[index][1], people[index]);
                index++;
            }
            return ans;
        }
    

    Leetcode #121 买卖股票的最佳时机

    题名:买卖股票的最佳时机
    描述:
    给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。

    如果你最多只允许完成一笔交易(即买入和卖出一支股票),设计一个算法来计算你所能获取的最大利润。

    注意你不能在买入股票前卖出股票。

    Example1

    
    输入: [7,1,5,3,6,4]
    输出: 5
    解释: 在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。
         注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格。
    

    Example2

    
    输入: [7,6,4,3,1]
    输出: 0
    解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。
    

    具体描述请查看Leetcode相关网页:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock/

    方法:贪心

    我总是期望在股票价格最低的时候入手,在最高的时候抛出。

    
        int maxProfit(vector<int>& prices) {
            if(!prices.size())  return 0;
            int shareVal = prices[0], maxP = 0;
            for(int i = 0;i < prices.size();i++){
                if(shareVal > prices[i]){//如果有价值更低的股票,我就入手
                    shareVal = prices[i];
                }
                else if(shareVal < prices[i]){//如果有股票价格变高了我就抛出
                    int tmp = prices[i] - shareVal;
                    if(tmp > maxP)  maxP = tmp;//记录下最大利益
                }
            }
            return maxP;
        }
    

    Leetcode #122 买卖股票的最佳时机 II

    题名:买卖股票的最佳时机 II
    描述:
    给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。

    设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。

    注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)

    Example1

    
    输入: [7,1,5,3,6,4]
    输出: 7
    解释: 在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。
         随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6-3 = 3 。
    

    Example2

    
    输入: [1,2,3,4,5]
    输出: 4
    解释: 在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。
         注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出。
         因为这样属于同时参与了多笔交易,你必须在再次购买前出售掉之前的股票。
    

    具体描述请查看Leetcode相关网页:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-ii/

    方法:贪心

    股票买卖策略:
    1.单独交易日:设今天价格p1, 明天价格p2,则今天买入,明天卖出可赚取金额 p2 - p1 (负值代表亏损)。
    2.连续上涨交易日:设此上涨交易日股票价格分别为 'p1, p2, p3,..., pn',则第一天买最后一天麦受益最大。即 `pn - p1 = (p2 - p1) + (p3 - p2) +...+(pn - pn-1)。
    3.连续下降交易日:则不买卖收益最大,即不会亏钱。
    代码如下:

    
        int maxProfit(vector<int>& prices) {
            int res = 0;
            for(int i = 1;i < prices.size();i++){
                int profit = prices[i] - prices[i - 1];
                if(profit > 0)  res += profit;
            }
            return res;
        }
    

    Leetcode #392 判断子序列

    题名:判断子序列
    描述:
    给定字符串 s 和 t ,判断 s 是否为 t 的子序列。

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

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

    Example1

    s = "abc", t = "ahbgdc"
    
    返回 true.
    

    Example2

    s = "axc", t = "ahbgdc"
    
    返回 false.
    

    具体描述请查看Leetcode相关网页:https://leetcode-cn.com/problems/is-subsequence/

    方法:从头到尾遍历

    双指针,用sp指向段字符串,tp指向长字符串。tp不断向后移,匹配到相等的sp向后移,因为不是严格意义上的子串,只要顺序一致就可以,所以sp不需要回溯。用sp是否超出s字符串长度来判断是否找到子串。代码如下:

    
        bool isSubsequence(string s, string t) {
            string::iterator sp ,tp;
            sp = s.begin(); tp = t.begin();
            while(sp != s.end() && tp != t.end()){
                if(*sp == *tp){
                    sp++;
                }
                tp++;
            }
            return sp == s.end();
        }
    

    Leetcode #665 非递减数列

    题名:非递减数列
    描述:
    给你一个长度为 n 的整数数组,请你判断在 最多 改变 1 个元素的情况下,该数组能否变成一个非递减数列。

    我们是这样定义一个非递减数列的: 对于数组中所有的 i (1 <= i < n),总满足 array[i] <= array[i + 1]。

    方法:贪心

    这种问题首先需要对特殊数据进行分析,从特殊到一般,总结出规律来,在进行编程!
    数据一: 4, 2, 3
    1.对于这串数据,当比较到4, 2时,出现了4 > 2的情况,那么是把4改成2还是把2改成4呢?我们人类很好判断出只要把4改成<=2的数即可;因为后面出现了3,把2改成4的话,后面的数就不符合了!这里就总结出了一条规律:> 当nums[i - 1] > nums[i]时,并且i - 1是第一个数时,令nums[i - 1] = nums[i];
    2.并且总结处到出现nums[i - 1] > nums[i]的情况时,需要考虑到底是令nums[i - 1] = nums[i],还是nums[i] = nums[i - 1]
    数据二: 3, 4, 2, 4
    1.当我们比较到4, 2时,出现了nums[i - 1] > nums[i]的情况,如果令nums[i - 1] = nums[i],就变成了3, 2, 2, 4明显不对;如果令nums[i] = nums[i - 1],就变成了3, 4, 4, 4就对了;从这里我们发现nums[i] 不仅要与 nums[i - 1]比较还要跟nums[i - 2]比较;

    从以上的归纳得出的结论:
    1.出现nums[i - 1] > nums[i]需要将nums[i]nums[i - 1]nums[i - 2]进行比较;不然的话直接令i++;
    2.当nums[i - 2] > nums[i - 1]时,令nums[i] = nums[i - 1];反之令令nums[i - 1] = nums[i]

    代码如下:

    
        bool checkPossibility(vector<int>& nums) {
            int count = 1, end = nums.size();
            for(int i= 1;i < end;i++){
                if(nums[i - 1] <= nums[i])  continue;
                count--;
                if(i - 2 >= 0 && nums[i - 2] > nums[i])
                    nums[i] = nums[i - 1];
                else  nums[i - 1] = nums[i];
                if(count < 0)   return false;
            }
            return true;
        }
    

    Leetcode #763 划分字母区间

    题名:划分字母区间
    描述:
    字符串 S 由小写字母组成。我们要把这个字符串划分为尽可能多的片段,同一个字母只会出现在其中的一个片段。返回一个表示每个字符串片段的长度的列表。

    Example1

    输入: S = "ababcbacadefegdehijhklij"
    输出: [9,7,8]
    解释:
    划分结果为 "ababcbaca", "defegde", "hijhklij"。
    每个字母最多出现在一个片段中。
    像 "ababcbacadefegde", "hijhklij" 的划分是错误的,因为划分的片段数较少。
    

    具体描述请查看Leetcode相关网页:https://leetcode-cn.com/problems/partition-labels/

    方法:贪心 + STL库的利用

    要想划分的片段最多,那么就是利用贪心的思想,当划分出的字符串s1, s1里面的字符不再次在S中出现时,立马就将其从整个S中划分出来。
    利用STL中的set和string容器。
    代码如下:

    
        vector<int> partitionLabels(string S) {
            vector<int> ans;
            set<char> st;//set是集合,在这个容器里面没有重复的元素
            string str;//利用好string容器的find方法;
            int index = 0;
            while(index < S.size()){
                bool flag = true;
                str += S[index];
                st.insert(S[index]);
                for(auto a : st){
                    if(index + 1 < S.size() && S.find(a, index + 1) != string::npos){  //size_t string::find(const char c, size_t pos) const noexcept;
                        flag = false;
                        break; 
                    }
                }
                if(flag){
                    ans.push_back(str.size());
                    str.clear();
                }
                index++;
            }
            return ans;
        }
    
  • 相关阅读:
    家庭作业汇总
    20145238荆玉茗 《信息安全系统设计基础》第5周学习总结
    20145238《信息安全系统设计基础》第3周学习总结
    20145238 《信息安全系统设计基础》第2周学习总结
    20145212 《信息安全系统设计基础》第11周学习总结
    20145212 《信息安全系统设计基础》第10周学习总结
    20145204&20145212信息安全系统实验三报告
    20145212&20145204信息安全系统实验五
    20145212 《信息安全系统设计基础》第9周学习总结
    20145204&20145212信息安全系统实验一
  • 原文地址:https://www.cnblogs.com/Codroc/p/12467185.html
Copyright © 2020-2023  润新知