算法经典面试题(8)——贪心算法

*题目编号为Leetcode中对应的题号。
某位大佬的Leetcode题解参考链接

贪心算法

  • 贪心算法的难点在于判断一个问题是否能用贪心算法解决。
  • 贪心算法离不开排序。
  1. (455 分发饼干) 假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。对每个孩子 i ,都有一个胃口值 gi ,这是能让孩子们满足胃口的饼干的最小尺寸;并且每块饼干 j ,都有一个尺寸 sj 。如果 sj >= gi ,我们可以将这个饼干 j 分配给孩子 i ,这个孩子会得到满足。你的目标是尽可能满足越多数量的孩子,并输出这个最大数值。

    注意:

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

    示例 1:

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

    示例 2:

    输入: [1,2], [1,2,3]
    
    输出: 2
    
    解释: 
    你有两个孩子和三块小饼干,2个孩子的胃口值分别是1,2。
    你拥有的饼干数量和尺寸都足以让所有孩子满足。
    所以你应该输出2.
    
    class Solution {
          
          
    public:
        int findContentChildren(vector<int>& g, vector<int>& s) {
          
          
    		if(g.empty() || s.empty())
                return 0;
            
            // 从大到小排序
            sort(g.begin(),g.end(),greater<int>());
            sort(s.begin(),s.end(),greater<int>());
            
            int gi=0, si=0;
            int res=0;
            while(gi<g.size() && si<s.size()){
          
          
                if(s[si] >= g[gi]){
          
          
                    si++;
                    gi++;
                    res++;
                }
                else // s[si] < g[gi]
                	 gi++;
            }
            return res;
        }
    };
    
  2. (392判断子序列) 给定字符串 st ,判断 s 是否为 t 的子序列。

    示例 1:
    s = "abc", t = "ahbgdc"

    返回 true.

    s = "axc", t = "ahbgdc"

    返回 false.

    /// Simulation
    /// Time Complexity: O(len(s) + len(t))
    /// Space Complexity: O(1)
    class Solution {
          
          
    public:
        bool isSubsequence(string s, string t) {
          
          
    
            int index = -1;
            for(char c: s){
          
          
                index = t.find(c, index + 1);
                if(index == string::npos) return false;
            }
            return true;
        }
    };
    
  3. (435 无重叠区间) 给定一个区间的集合,找到需要移除区间的最小数量,使剩余区间互不重叠。

    注意:

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

    示例 1:

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

    示例 2:

    输入: [ [1,2], [1,2], [1,2] ]
    
    输出: 2
    
    解释: 你需要移除两个 [1,2] 来使剩下的区间没有重叠。
    
    // 动态规划实现
    bool compare(const vector<int> &a, const vector<int> &b){
          
          
        if(a[0] != b[0])
    		return a[0] < b[0];
        return a[1] < b[1];
    }
    
    class Solution {
          
          
    public:
        int eraseOverlapIntervals(vector<vector<int>>& intervals) {
          
          
    		if(intervals.empty())
                return 0;
            
            sort(intervals.begin(), intervals.end(), compare);
            
            // memo[i]表示以第i个区间为结尾的最长上升子区间的长度
            vector<int> memo(intervals.size(), 1);
            for(int i=0;i<intervals.size();i++){
          
          
                for(int j=0;j<i;j++){
          
          
                    if(intervals[i][0]>=intervals[j][1])
                        memo[i]=max(memo[i], 1+memo[j]);
                }
            }
            int res=0;
            for(int i=0;i<memo.size();i++)
                res=max(res,memo[i]);
            
            return intervals.size()-res;
        }
    };
    
    • 每次选择中,每个区间的结尾很重要,结尾越小,留给后面的空间越大,后面越有可能容纳更大的空间。
    • 所以,按照区间的结尾排序。每次选择结尾最早的,且和前一个区间不重叠的区间。
    // 贪心算法实现
    bool compare(const vector<int> &a, const vector<int> &b){
          
          
        if(a[1] != b[1])
    		return a[1] < b[1];
        return a[0] < b[0];
    }
    
    class Solution {
          
          
    public:
        int eraseOverlapIntervals(vector<vector<int>>& intervals) {
          
          
    		if(intervals.empty())
                return 0;
            
            sort(intervals.begin(), intervals.end(), compare);
            
            int pre=0
            int res=1;
            for(int i=1;i<intervals.size();i++){
          
          
                if(itervals[i][0]>=intervals[pre][1]){
          
          
                    pre=i;
                    res++;
                }
            }
            
            return intervals.size()-res;
        }
    };
    
  • 贪心选择性质:在求解一个最优化的过程中,使用贪心的方式选择了一组内容之后,不会影响下面的子问题的求解。或者说一个全局最优解可以通过局部最优得到,即存在一个最优解是以贪心选择开始的。
  • 贪心选择性质证明:贪心算法为A,假设的最优算法为O;发现A完全能替代O,且不影响求出最优解

猜你喜欢

转载自blog.csdn.net/qq_34731182/article/details/113685954