编程题——贪心算法(leetcode简单篇 C++版)

 

目录

 

分发饼干

132模式

买股票的最佳时期II

删除造序

柠檬水找零


  • 分发饼干

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

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

示例一:

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

输出: 1

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

示例二:

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

输出: 2

解释: 
你有两个孩子和三块小饼干,2个孩子的胃口值分别是1,2。
你拥有的饼干数量和尺寸都足以让所有孩子满足。
所以你应该输出2.

解题代码:

#include

using namespace std;
class Solution {
public:
    int findContentChildren(vector& g, vector& s) {
        sort(g.begin(), g.end());
        sort(s.begin(), s.end());
        int count = 0; 
        
        for(int i = 0, j = 0; i < g.size() && j < s.size(); ){
            if(g[i] <= s[j]){
                count++;
                i++;
                j++;
            }else{
                j++;
            }
        }
        return count;
    }
};

解题思路:

  1. 首先利用库函数algorithm的sort来对两个vector进行排序。
  2. 然后两个数组利用外排的方法遍历。
  3. 遍历过程,满足条件就count++。
  4. 这个for循环比较特殊,有两个索引值i,j。

  • 132模式

题目描述:给定一个整数序列:a1, a2, ..., an,一个132模式的子序列 ai, aj, ak 被定义为:当 i < j < k 时,ai < ak < aj。设计一个算法,当给定有 n 个数字的序列时,验证这个序列中是否含有132模式的子序列。

注意:n 的值小于15000。

示例一:

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

输出: False

解释: 序列中不存在132模式的子序列。

示例二:

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

输出: True

解释: 序列中有 1 个132模式的子序列: [1, 4, 2].

示例三:

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

输出: True

解释: 序列中有 3 个132模式的的子序列: [-1, 3, 2], [-1, 3, 0] 和 [-1, 2, 0].

解题代码一:

class Solution {
public:
    bool find132pattern(vector& nums) {
        int size = nums.size();
        int pre = INT_MAX;
        
        for(int i = 0; i  i; j--){
                if((nums[j] > pre) && (nums[j] < nums[i])){
                    return true;
                }
            }
        } 
        return false;
    }
};

解题思路:

  1. 关键在于寻找驼峰。
  2. 先设置pre变量,保存了上一个比当前数字小的数字,那么当前数字作为驼峰,然后去寻找下一个比驼峰小,比pre大的数字,找到即返回true,否则一直找下去,找完所有也没有符合条件的这个数或者是驼峰,就跳出循环,返回false。

解题代码二:

class Solution {
public:
    bool find132pattern(vector& nums) {
        int size = nums.size();
        int aft = INT_MIN;
        stack s;        
        for(int i = size - 1; i >= 0; i--){
            if(aft >  nums[i]) return true;
            while(!s.empty() && nums[i] > s.top()){
                aft = s.top();
                s.pop();
            }
            s.push(nums[i]);
        } 
        return false;
    }
};

解题思路:

  1. 这个代码比上一个代码用时更少,但是思维强度更大。
  2. 记下这个用法。

  • 买股票的最佳时期II

题目描述:给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。

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

示例1:

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

示例2:

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

示例3:

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

解题代码一(峰谷法):

class Solution {
public:
    int maxProfit(vector& prices) {
        int size = prices.size();
        int peak = 0, valley = 0, profit = 0, index = 0;
        while(index < size - 1){
            //找波谷
            while(index < size - 1 && prices[index] >= prices[index+1]){
                index++;
            }
            valley = prices[index];
            //找波峰
            while(index < size - 1 && prices[index] <= prices[index+1]){
                index++;
            }
            peak = prices[index];
            
            profit += (peak - valley);
        }
        return profit;
    }
};

解题思路:

  1. 这个代码采用的是峰谷法。
  2. 把数组中的数字以折线图的形式反映,我们发现这就是有波峰和波谷的折线图。而我们要找最大利润,其实就是我们要找到所有挨得最近的一组波谷和波峰差值的总和。
  3. 在这种情况下:只需要一次循环,不停的找某个波谷的值,然后紧接着找到离它最近的一个波峰的值,他们的差值就是最大利润的一部分;然后接下去再找下一组波谷和波峰。
  4. 时间复杂度:O(N)

解题代码二(一次遍历法):

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

解题思路:

  1. 这种方法是峰谷法的改进
  2. 在从头到尾循环遍历数组中每一个元素的时候,我们可以把处于升序排列的子序列数组元素的差值依次相加。
  3. 关于2这一点怎么得出来的,这个是观察数组得出来的结论。并且这个结论可以记下来。

  • 删除造序

题目描述:

给定由 N 个小写字母字符串组成的数组 A,其中每个字符串长度相等。

选取一个删除索引序列,对于 A 中的每个字符串,删除对应每个索引处的字符。 所余下的字符串行从上往下读形成列。

比如,有 A = ["abcdef", "uvwxyz"],删除索引序列 {0, 2, 3},删除后 A 为["bef", "vyz"], A 的列分别为["b","v"], ["e","y"], ["f","z"]。(形式上,第 n 列为 [A[0][n], A[1][n], ..., A[A.length-1][n]])。

假设,我们选择了一组删除索引 D,那么在执行删除操作之后,A 中所剩余的每一列都必须是 非降序 排列的,然后请你返回 D.length 的最小可能值。

示例一:

输入:["cba", "daf", "ghi"]
输出:1
解释:
当选择 D = {1},删除后 A 的列为:["c","d","g"] 和 ["a","f","i"],均为非降序排列。
若选择 D = {},那么 A 的列 ["b","a","h"] 就不是非降序排列了。

示例二:

输入:["a", "b"]
输出:0
解释:D = {}

示例三:

输入:["zyx", "wvu", "tsr"]
输出:3
解释:D = {0, 1, 2}

提示:

  1. 1 <= A.length <= 100
  2. 1 <= A[i].length <= 1000

解题代码:

class Solution {
public:
    int minDeletionSize(vector& A) {
        int count = 0;
        for(int i = 0; i < A[0].length(); i++){
            for(int j = 0; j < A.size() - 1; j++){
                if(A[j][i] > A[j+1][i]){
                    count++;
                    break;
                }
            }
        }
        return count;
    }
};

解题思路:

  1. 看懂题意
  2. 然后遍历数组的每个字符串,并遍历字符串中的每一个字符

  • 柠檬水找零

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

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

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

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

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

示例一:

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

示例二:

输入:[5,5,10]
输出:true

示例三:

输入:[10,10]
输出:false

示例四:

输入:[5,5,10,10,20]
输出:false
解释:
前 2 位顾客那里,我们按顺序收取 2 张 5 美元的钞票。
对于接下来的 2 位顾客,我们收取一张 10 美元的钞票,然后返还 5 美元。
对于最后一位顾客,我们无法退回 15 美元,因为我们现在只有两张 10 美元的钞票。
由于不是每位顾客都得到了正确的找零,所以答案是 false。

提示:

  1. 0 <= bills.length <= 10000
  2. bills[i] 不是 5 就是 10 或是 20 

解题代码:

class Solution {
public:
    bool lemonadeChange(vector& bills) {
        int count_5 = 0;
        int count_10 = 0;
        
        for(int i = 0 ; i < bills.size(); i++){
            if(bills[i] == 5){
                count_5++;
            }else if(bills[i] == 10){
                if(count_5 > 0){
                    count_5--;
                    count_10++;
                }else{
                    return false;
                }
            }else{
                if(count_5 > 0 && count_10 > 0){ //这个条件一定要在前面
                    count_5--;
                    count_10--;
                }else if(count_5 >= 3){
                    count_5 -= 3;
                }else{
                    return false;
                }
            }
        }
        return true;
    }
};

解题思路:

  1. 常规解题思路,遍历数组中的每一个数的时候,计算当前5块钱和10块钱有几张。如果当前数是5,则count_5+1;如果当前数是10,计算当前有几张5块钱,如果至少有一张,则找零count_10+1,count_5-1,如果没有5块钱了,直接返回false;如果当前数是20,则首先计算当前有几张10块钱和几张5块钱,满足则count_10 - 1, count5 - 1,如果不满足,则计算5块钱是否大于等于3张,满足 count_5 - 3,不满足就返回false。
  2. 这道题,其他没有太大逻辑上的难点,但有一个易错点,就是对于20块钱,找零首先是看10块有没有,如果10块没有,再找5。因为如果先找3张5块,后面很可能遇到10块就没有5块了,就会返回false,导致程序结果不对。

 

你可能感兴趣的:(C/C++,leetcode,简单篇)