LeetCode题解——贪心算法

会有一股神秘感。 提交于 2019-12-01 01:38:32

贪心算法

关于贪心算法,我们先看一个例子。

假设我们有一个可以容纳 100kg 物品的背包,可以装各种物品。我们有以下 5 种豆子,每种豆子的总量和总价值都各不相同。为了让背包中所装物品的总价值最大,我们如何选择在背包中装哪些豆子?每种豆子又该装多少呢?

实际上,这个问题很简单,我估计你一下子就能想出来,没错,我们只要先算一算每个物品的单价,按照单价由高到低依次来装就好了。单价从高到低排列,依次是:黑豆、绿豆、红豆、青豆、黄豆,所以,我们可以往背包里装 20kg 黑豆、30kg 绿豆、50kg 红豆。

这个问题的解决思路显而易见,它本质上借助的就是贪心算法。结合这个例子,我总结一下贪心算法解决问题的步骤,我们一起来看看。

第一步,当我们看到这类问题的时候,首先要联想到贪心算法:针对一组数据,我们定义了限制值和期望值,希望从中选出几个数据,在满足限制值的情况下,期望值最大。

类比到刚刚的例子,限制值就是重量不能超过 100kg,期望值就是物品的总价值。这组数据就是 5 种豆子。我们从中选出一部分,满足重量不超过 100kg,并且总价值最大。

第二步,我们尝试看下这个问题是否可以用贪心算法解决:每次选择当前情况下,在对限制值同等贡献量的情况下,对期望值贡献最大的数据。

类比到刚刚的例子,我们每次都从剩下的豆子里面,选择单价最高的,也就是重量相同的情况下,对价值贡献最大的豆子

第三步,我们举几个例子看下贪心算法产生的结果是否是最优的大部分情况下,举几个例子验证一下就可以了。严格地证明贪心算法的正确性,是非常复杂的,需要涉及比较多的数学推理。而且,从实践的角度来说,大部分能用贪心算法解决的问题,贪心算法的正确性都是显而易见的,也不需要严格的数学推导证明。

实际上,用贪心算法解决问题的思路,并不总能给出最优解,而是局部最优解。例如用贪心算法求最短路径。

算法实战——455.分发饼干

注意:

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

示例 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.

暴力解法

暴力解法很容易想到,优先满足胃口小的小朋友的需求。对s,g升序,然后把小孩分配给饼干,小孩子满足计数加1。flag[]数组用于标记饼干是否已分配。

class Solution {
public:
    int findContentChildren(vector<int>& g, vector<int>& s) {
        int gsize = g.size();
        int size = s.size();
        sort(g.begin(), g.end());
        sort(s.begin(), s.end());
        int ans = 0;
        bool flag[size+1] = {false};
        bool flag1 = 0;
        for(int i = 0 ; i < gsize; ++i)
        {
            if(i < size)
            {
                for(int j = i; j < size; ++j)
                {
                    if(s[j] >= g[i] && flag[j] == false)
                    {
                        ans++;
                        flag[j] = true;
                        break;
                    }
                    if(j == size - 1)
                    {
                        flag1 = 1;
                    }
                }
            }
            else
            {
                break;
            }
            if(flag1 == 1)
            {
                break;
            }
        }
        return ans;
    }
};

时间复杂度:O(n^2)
空间复杂度:O(n)

优化算法

优先满足胃口小的小朋友的需求。

对 g 和 s 升序排序
初始化两个指针 i 和 j 分别指向 g 和 s 初始位置
对比 g[i] 和 s[j]
g[i] <= s[j]:饼干满足胃口,把能满足的孩子数量加 1,并移动指针 i = i + 1,j = j + 1
g[i] > s[j]:无法满足胃口,j 右移,继续查看下一块饼干是否可以满足胃口

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

时间复杂度:O(n)
空间复杂度:O(1)
在这里插入图片描述

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!