当前位置: 首页 > article >正文

刷题记录 贪心算法-2:455. 分发饼干

题目:455. 分发饼干

难度:简单

假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。

对每个孩子 i,都有一个胃口值 g[i],这是能让孩子们满足胃口的饼干的最小尺寸;并且每块饼干 j,都有一个尺寸 s[j] 。如果 s[j] >= g[i],我们可以将这个饼干 j 分配给孩子 i ,这个孩子会得到满足。你的目标是满足尽可能多的孩子,并输出这个最大数值。

示例 1:

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

示例 2:

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

提示:

  • 1 <= g.length <= 3 * 104
  • 0 <= s.length <= 3 * 104
  • 1 <= g[i], s[j] <= 231 - 1

一、模式识别

1.贪心算法

局部最优解:找到满足胃口最小(大)的孩子的最小(大)饼干

从局部到全局:每满足一个孩子继续用剩余的饼干满足剩余的孩子

反例:想不到。。。

2.双指针

根据题干条件想到双指针并不难,其实我一开始是用双指针的思路做的:

题目条件:两个数组 + 逐个数字比大小(s[j] >= g[i]) -> 解法:双指针 

官方解法中的解释是:排序 + 双指针 + 贪心:

为了尽可能满足最多数量的孩子,从贪心的角度考虑,应该按照孩子的胃口从小到大的顺序依次满足每个孩子,且对于每个孩子,应该选择可以满足这个孩子的胃口且尺寸最小的饼干。

还给出了证明,但我觉得思路上理解起来并不难,所以证明过程不用细看

二.代码实现

1.双指针 + 从小胃口孩子开始喂(最简洁实现)

步骤:1.排序  2.双指针  3.循环数字比大小

写法有while循环写法和for循环写法,推荐while循环写法,可读性较强,

可以理解为for循环写法衍生于while循环写法

while循环写法:

class Solution:
    def findContentChildren(self, g: List[int], s: List[int]) -> int:
        if not s:
            return 0
        m, n = len(g), len(s)
        g.sort()
        s.sort()
        i, j = 0, 0
        while i < n and j < m:
            if s[i] >= g[j]:
                j += 1
            i += 1
        return j

for循环写法:

class Solution:
    def findContentChildren(self, g: List[int], s: List[int]) -> int:
        if not s:
            return 0
        m, n = len(g), len(s)
        s.sort()
        g.sort()
        j = 0
        for i in range(n):
            if j < m and s[i] >= g[j]:
                j += 1
        return j
  • 时间复杂度:O(nlogn)
  • 空间复杂度:O(1)

2.双指针 + 从大胃口孩子开始喂

 步骤:1.排序  2.双指针  3.循环数字比大小

不同于从小胃口孩子喂,除了倒序访问的区别外,

从大胃口孩子开始喂不能直接返回索引,因此多了一个变量ans

和从小胃口一样,写法有while循环写法和for循环写法,推荐while循环写法,可读性较强,

可以理解为for循环写法衍生于while循环写法

while循环写法:

class Solution:
    def findContentChildren(self, g: List[int], s: List[int]) -> int:
        if not s:
            return 0
        g.sort()
        s.sort()
        m, n = len(g), len(s)
        i, j = n - 1, m - 1
        ans = 0
        while i >= 0 and j >= 0:
            if s[i] >= g[j]:
                i -= 1
                ans += 1
            j -= 1
        return ans

for循环写法:

class Solution:
    def findContentChildren(self, g: List[int], s: List[int]) -> int:
        if not s:
            return 0
        g.sort()
        s.sort()
        m, n = len(g), len(s)
        i = n - 1
        ans = 0
        for j in range(m - 1, -1, -1):
            if i >= 0 and s[i] >= g[j]:
                i -= 1
                ans += 1
        return ans
  • 时间复杂度:O(nlogn)
  • 空间复杂度:O(1)

3.可以用栈/队列代替双指针

实现的逻辑相同,就是用栈/队列的抛出操作代替了移动指针操作,但队列比指针操作耗时略大:

从小胃口孩子开始喂(栈):

class Solution:
    def findContentChildren(self, g: List[int], s: List[int]) -> int:
        if not s:
            return 0
        ans = 0
        g.sort()
        s.sort()
        while g and s:
            ch_g = g.pop()
            if s[-1] >= ch_g:
                s.pop()
                ans += 1
        return ans

从大胃口孩子开始喂(队列):

class Solution:
    def findContentChildren(self, g: List[int], s: List[int]) -> int:
        if not s:
            return 0
        ans = 0
        g.sort()
        s.sort()
        gdeque = deque(g)
        sdeque = deque(s)
        while gdeque and sdeque:
            ch_s = sdeque.popleft()
            if ch_s >= gdeque[0]:
                gdeque.popleft()
                ans += 1
        return ans
  • 时间复杂度:O(nlogn)
  • 空间复杂度:O(n)

三、为什么两种实现方式遍历的数组不同

模式识别目的是快速找到一个解法,

但为了满足自己好奇心以及加快以后的模式识别,我想探究一下这个问题

配对条件是饼干大小s[j] >= 胃口g[i],即饼干大于胃口

从小到大配对时,需要遍历要求较大者饼干,保证所有大饼干配对

即从小到大遍历要求较大者(饼干),防止小饼干无法配对导致错过大饼干,

如果遍历胃口,面临饼干组左端尺寸过小的饼干时,无法继续找更大的饼干

从大到小配对时,需要遍历要求较小者胃口,保证所有小胃口配对

即从大到小遍历要求较小者(胃口),防止大胃口无法配对导致错过小胃口,

如果遍历饼干,面临胃口组右端胃口过大的孩子时,无法继续找更小的胃口


http://www.kler.cn/a/519811.html

相关文章:

  • 【人工智能】循环神经网络学习
  • 【练习】PAT 乙 1020 月饼
  • 单片机基础模块学习——数码管(二)
  • 微信阅读网站小程序的设计与实现(LW+源码+讲解)
  • OLMo:开启AI研究新纪元的开放利器
  • 基于java线程池和EasyExcel实现异步导出
  • 如何使用Java爬虫获取AliExpress商品详情:代码示例与实践指南
  • python爬虫框架Scrapy简介
  • C#牵手Blazor,解锁跨平台Web应用开发新姿势
  • 机器人学习的范式转变:从专用走向通用基础模型
  • C# 中使用Hash用于密码加密
  • AI Agent的多轮对话:提升用户体验的关键技巧
  • Linux之Tcp粘包笔记
  • Oracle之Merge into函数使用
  • 蓝桥杯LQ1044 求完数
  • 不同路径(62)
  • 机器学习 ---逻辑回归
  • 手撕B-树
  • python学opencv|读取图像(四十五)增加掩模:使用cv2.bitwise_and()函数实现图像按位与运算
  • 修改 Go 版本后不生效?深入排查与解决方案