• LeetCode - 1700. 无法吃午餐的学生数量


    学校的自助午餐提供圆形和方形的三明治,分别用数字 0 和 1 表示。所有学生站在一个队列里,每个学生要么喜欢圆形的要么喜欢方形的。
    餐厅里三明治的数量与学生的数量相同。所有三明治都放在一个 栈 里,每一轮:

    如果队列最前面的学生 喜欢 栈顶的三明治,那么会 拿走它 并离开队列。
    否则,这名学生会 放弃这个三明治 并回到队列的尾部。
    这个过程会一直持续到队列里所有学生都不喜欢栈顶的三明治为止。

    给你两个整数数组 students 和 sandwiches ,其中 sandwiches[i] 是栈里面第 i​​​​​​ 个三明治的类型(i = 0 是栈的顶部), students[j] 是初始队列里第 j​​​​​​ 名学生对三明治的喜好(j = 0 是队列的最开始位置)。请你返回无法吃午餐的学生数量。

    示例 1:

    输入:students = [1,1,0,0], sandwiches = [0,1,0,1]
    输出:0 
    解释:
    - 最前面的学生放弃最顶上的三明治,并回到队列的末尾,学生队列变为 students = [1,0,0,1]。
    - 最前面的学生放弃最顶上的三明治,并回到队列的末尾,学生队列变为 students = [0,0,1,1]。
    - 最前面的学生拿走最顶上的三明治,剩余学生队列为 students = [0,1,1],三明治栈为 sandwiches = [1,0,1]。
    - 最前面的学生放弃最顶上的三明治,并回到队列的末尾,学生队列变为 students = [1,1,0]。
    - 最前面的学生拿走最顶上的三明治,剩余学生队列为 students = [1,0],三明治栈为 sandwiches = [0,1]。
    - 最前面的学生放弃最顶上的三明治,并回到队列的末尾,学生队列变为 students = [0,1]。
    - 最前面的学生拿走最顶上的三明治,剩余学生队列为 students = [1],三明治栈为 sandwiches = [1]。
    - 最前面的学生拿走最顶上的三明治,剩余学生队列为 students = [],三明治栈为 sandwiches = []。
    所以所有学生都有三明治吃。

    示例 2:

    输入:students = [1,1,1,0,0,1], sandwiches = [1,0,0,0,1,1]
    输出:3

    链接:https://leetcode.cn/problems/number-of-students-unable-to-eat-lunch
     


    最好想的方式就是按照题目模拟这个过程,

    1. 让每个三明治和学生进行匹配
      1. 匹配成功,学生出队列,三明治出队列
      2. 匹配失败,学生从队首移到队尾,三明治不动,继续下一个学生

    还有一点要注意的的是,结束循环的条件有2种:

    1. 当剩余的学生经过一次循环后,都没有匹配成功,说明这个三明治无人匹配成功,应该结束所有循环,剩余的学生数量就是无法吃午餐的数量
    2. 三明治都吃完了,也是结束循环的条件
    1. class Solution {
    2. func countStudents(_ students: [Int], _ sandwiches: [Int]) -> Int {
    3. var sandwichesArray = sandwiches
    4. var studentsArray = students
    5. while true {
    6. // 最顶部的三明治
    7. let topSandwich = sandwichesArray[0]
    8. let result = canFindStudent(studentsArray, topSandwich)
    9. // 三明治能找到学生
    10. if result.canFind {
    11. sandwichesArray.remove(at: 0)
    12. studentsArray = result.students
    13. } else {
    14. // 三明治不能找到学生,结束循环
    15. break
    16. }
    17. // 三明治为空, 结束循环
    18. if sandwichesArray.count == 0 {
    19. break
    20. }
    21. }
    22. return studentsArray.count
    23. }
    24. /// 最顶部的三明治能不能找到合适的学生
    25. /// - Parameters:
    26. /// - students: 学生数组
    27. /// - sandwiches: 顶部的三明治
    28. /// - Returns: YES: 能找到; NO: 不能找到; 修改后的学生数组
    29. func canFindStudent(_ students: [Int], _ topSandwich: Int) -> (canFind: Bool,students: [Int]) {
    30. // 临时队列, 修改学生信息
    31. var tempStudentArray = students
    32. // 默认不能找到
    33. var canFind = false
    34. for oneStudent in students {
    35. // 匹配成功, 返回结果
    36. if oneStudent == topSandwich {
    37. tempStudentArray.remove(at: 0)
    38. canFind = true
    39. break
    40. } else {
    41. // 匹配不成功,移到队尾, 继续下一次
    42. let topStudent = tempStudentArray.remove(at: 0)
    43. tempStudentArray.append(topStudent)
    44. }
    45. }
    46. return (canFind, tempStudentArray)
    47. }
    48. }

    假设N是学生数组的数量,由于三明治数量和学生数量一致,所以也是N

    算法最差的情况下需要O(N^2),  最好的情况下需要O(N), 和学生的顺序有关。


     还有一种思路是采用整体思考的方式, 由于学生不吃三明治会从队首移动到队尾,学生的顺序并不影响分配三明治,

    出现无法吃午餐,一定是下面2种情况之一:

    1. 顶部的三明治为0, 剩余学生全部为1;
    2. 顶部的三明治为1,剩余学生全部为0;

    学生的顺序不影响分配三明治, 用一个字典统计出学生对于三明治的数量,key为三明治的类型,  value为吃此三明治的学生数量, 经过一轮循环后,字典内的最终结果为{0:x个, 1:y个},

    然后遍历三明治,每个三明治都从字典中移除一个学生,结束的条件就是

    1. 某个三明治对应的字典中学生数量为0,表示无人吃此三明治
    2. 三明治全部遍历完成

    用字典是为了方便拓展, 比如三明治的品类变多,或者变成了其他类型的食物, 如果只是为了这道题的话,用2个变量来记录也是可以的。

    1. class Solution {
    2. func countStudents(_ students: [Int], _ sandwiches: [Int]) -> Int {
    3. // 统计出学生对于三明治的数量,
    4. // 三明治为key,value为吃此三明治的学生数量,最终结果为{0:x个, 1:y个}
    5. // 用字典是为了方便拓展, 比如三明治的品类变多,或者变成了其他类型的食物
    6. var studentDic = [Int:Int]()
    7. for oneStu in students {
    8. if var count = studentDic[oneStu] {
    9. count += 1
    10. studentDic[oneStu] = count
    11. } else {
    12. studentDic[oneStu] = 1
    13. }
    14. }
    15. // 由于学生的顺序并不影响分配三明治,只要找到无人吃的三明治,剩余的学生数量就是所有的无法吃午餐的数量,同时由于学生数量和三明治数量是一致的,剩余的三明治数量也可以作为结果返回
    16. // 找出无法在匹配成功的三明治
    17. for (i,oneSandwiche) in sandwiches.enumerated() {
    18. // 三明治找学生,
    19. // 1.找到了,把此学生从字典中移除
    20. // 2.找不到,返回剩余的三明治数量,此数量和剩余学生数量是相等的
    21. var count = studentDic[oneSandwiche] ?? 0
    22. if count > 0 {
    23. count -= 1
    24. studentDic[oneSandwiche] = count
    25. } else {
    26. return sandwiches.count - i
    27. }
    28. }
    29. return 0
    30. }
    31. }

    此算法只需要2次遍历即可完成,时间复杂度为O(N), 并且和学生的顺序无关。

    综合来看,此算法更优秀一点。

  • 相关阅读:
    Git之merge与rebase操作命令及问题
    【Go】 力扣 - 剑指 Offer 第五天 - 二维数组中的查找
    【咖啡品牌分析】Google Maps数据采集咖啡市场数据分析区域分析热度分布分析数据抓取瑞幸星巴克
    力扣刷题 day43:10-13
    Learning Git Branch 题解(基础、高级、Git远程仓库)
    Docker常用命令记录
    A-Level Economics 真题及答案解析
    xerces-c++内存管理策略&为何耗费大量内存
    vscode px转换rem插件 px to rem & rpx & vw (cssrem)
    java的网络编程
  • 原文地址:https://blog.csdn.net/u014600626/article/details/127603035