LeetCode 455. 分发饼干

Posted 想名真难

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LeetCode 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.

455. 分发饼干 力扣


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

  1. 统计出每个胃口的孩子数量
  2. 统计出饼干大小的数量
  3. 对胃口和饼干大小做升序排列
  4. 遍历每个胃口,针对每个胃口处理
    1. 饼干大小 < 胃口
      1. 饼干大小调大
    2. 饼干大小 >= 胃口
      1. 如果此胃口对应的饼干数量 >= 孩子数,
        1. 结果 += 孩子数, 
        2. 此胃口剩余的饼干数量 = 饼干数 - 孩子数
      2. 如果此胃口对应的饼干数量 < 孩子数,
        1. 剩余待分配的孩子数 = 孩子数 -  此胃口对应的饼干数量
        2. 把饼干的大小调大,继续匹配,递归调用,直到剩余孩子数量为0,
  5. 返回结果
class Solution 

    var gDic : [Int:Int]!
    var sDic : [Int:Int]!

    var gKeyArray : [Int]!
    var sKeyArray : [Int]!
    var gIndex = 0
    var sIndex = 0

    var result = 0

    func findContentChildren(_ g: [Int], _ s: [Int]) -> Int 

        gDic = [Int:Int]()
        sDic = [Int:Int]()

        // 以胃口做key, value为这个胃口的人数
        for oneG in g 
            if let count = gDic[oneG] 
                gDic[oneG] = count + 1
             else 
                gDic[oneG] = 1
            
        
        // 以饼干大小做key, value为这个大小的饼干数量
        for oneS in s 
            if let count = sDic[oneS] 
                sDic[oneS] = count + 1
             else 
                sDic[oneS] = 1
            
        


        // 胃口和饼干都按照从小到大的顺序排列
        gKeyArray = gDic.keys.sorted()
        sKeyArray = sDic.keys.sorted()

        gIndex = 0
        sIndex = 0
        result = 0

        while gIndex < gKeyArray.count && sIndex < sKeyArray.count 
            // 饼干大小 < 胃口大小, 把饼干大小增加, 直到饼干大小 >= 胃口大小, 或者没有饼干,结束循环
            if sKeyArray[sIndex] < gKeyArray[gIndex] 
                sIndex += 1
             else 
                let studentCount = gDic[gKeyArray[gIndex]]!
                self.handleStu(studentCount: studentCount)
            
        
        return result
    

    // 给学生分配饼干
    func handleStu(studentCount : Int) 

        if studentCount <= 0 
            return
        
        if sIndex >= sKeyArray.count 
            return
        

        if let sValue = sDic[sKeyArray[sIndex]] 
            if sValue >= studentCount 
                result += studentCount
                let leftValue = sValue - studentCount
                sDic[sKeyArray[sIndex]] = leftValue
                gIndex += 1
             else 
                sDic[sKeyArray[sIndex]] = 0
                result += sValue
                // 先把sValue个饼干分给同胃口的学生, 剩余待分配的学生数量就是studentCount -= sValue
                let leftStudentCount = studentCount - sValue
                // sIndex持续往上增加,知道把此胃口的学生都分配完成
                sIndex += 1
                // 递归调用自己,继续分配,直到剩余学生数量为0或者没有饼干
                self.handleStu(studentCount: leftStudentCount)
            
        
    



 多次遍历数组+2次排序,总体的时间复杂度为O(N*logN), 需要额外的空间来存字典,空间复杂度为O(N).

虽然通过了所有用例, 但是时间上只超过26% , 可能会有更优的解法. 


看了一下题解,改进一下,其实没有必要来维护这2个字典,直接排序用原始数据计算即可。

  1. 首先还是排序,对胃口和饼干大小做升序排列,比如g=[7,7,8,8,9,9], s = [7,8,9]
  2. 用2个下标来记录分配到的位置
  3. 针对每个胃口处理
    1. 对应的饼干大小 > 胃口大小
      1. 结果 += 1
      2. 孩子后移1个, 饼干后移1个 
    2. 对应的饼干大小 < 胃口大小
      1. 饼干后移1个, 其余不动
class Solution 

    func findContentChildren(_ g: [Int], _ s: [Int]) -> Int 
        // 胃口和饼干都按照从小到大的顺序排列
        let gArray = g.sorted()
        let sArray = s.sorted()

        // 对应数组的下表
        var gIndex = 0
        var sIndex = 0
        // 总的匹配数
        var result = 0

        while gIndex < gArray.count && sIndex < sArray.count 

            // 饼干大小 < 胃口大小, 把饼干大小增加
            // 饼干大小 >= 胃口大小, 总数+1,饼干后移,学生后移
            if sArray[sIndex] < gArray[gIndex]   
                sIndex += 1
             else 
                sIndex += 1
                gIndex += 1
                result += 1
            
        
        return result
    

改进之后的算法看起来思路也清晰了很多, 代码行数也少了很多, 性能也比第一种提升很大.


跑下用例, 其实时间上就是100%, 只是LeetCode的机器有时快,有时慢,跑完用例的时间不一致导致,由于有排序操作和遍历操作,时间复杂度还是O(N*logN), 空间复杂度O(1),

以上是关于LeetCode 455. 分发饼干的主要内容,如果未能解决你的问题,请参考以下文章

LeetCode455. 分发饼干(C++)

LeetCode 455. 分发饼干

贪心算法——leetcode455.分发饼干

Leetcode刷题Python455.分发饼干

Leetcode刷题100天—455. 分发饼干(贪心)—day40

Leetcode刷题100天—455. 分发饼干(贪心)—day40