小C点单 | 豆包MarsCode AI刷题

3 阅读7分钟

小C点单

问题描述

小C来到了一家餐馆,准备点一些菜。

已知该餐馆有 n道菜,第 i道菜的售价为 wi。

小C准备点一些价格相同的菜,但小C不会点单价超过m的菜。

小C想知道,自己最多可以点多少道菜?

测试样例

样例1:

输入:m = 6, w = [2, 3, 3, 6, 6, 6, 9, 9, 23] 输出:3

样例2:

输入:m = 4, w = [1, 2, 4, 4, 4] 输出:3

样例3:

输入:m = 5, w = [5, 5, 5, 5, 6, 7, 8] 输出:4 from functools import reduce def solution(m: int, w: list) -> int: can_afford = list(filter(lambda x : x <= m, w)) # 可以点单的菜 counts = {} # 创建空字典 方便计数 for element in can_afford: counts[element] = counts.get(element, 0) + 1

max_count = reduce(lambda x, y: x if x > y else y, counts.values(), 0)
# write code here
return max_count

if name == 'main': print(solution(6, [2, 3, 3, 6, 6, 6, 9, 9, 23]) == 3) print(solution(4, [1, 2, 4, 4, 4]) == 3) print(solution(5, [5, 5, 5, 5, 6, 7, 8]) == 4)

counts[element] = counts.get(element, 0) + 1 

这句话:

counts[element] = counts.get(element, 0) + 1

用于统计 element 在字典 counts 中的出现次数。下面我会逐步解释它的含义。


分解解释

1. counts 是一个字典
  • counts 是一个字典,存储每个 element 作为键,出现的次数作为值。
  • 比如:{'a': 2, 'b': 1} 表示 'a' 出现了 2 次,'b' 出现了 1 次。
2. counts.get(element, 0)
  • 作用: 查找字典 counts 中键 element 的值。
  • 如果 element 存在于字典中,则返回其值(即当前的出现次数)。
  • 如果 element 不存在,则返回默认值 0
3. + 1
  • 在当前次数的基础上加 1,表示又发现了一个 element
4. 更新字典
  • counts[element] = ... 将字典中键 element 的值更新为新的次数。

逻辑总结

完整的逻辑可以分解为:

  1. 如果 element 不在 counts 中,counts.get(element, 0) 返回 0,于是 counts[element] = 0 + 1,即将 element 的值初始化为 1
  2. 如果 element 已经在 counts 中,比如值是 2,那么 counts.get(element, 0) 返回 2,于是 counts[element] = 2 + 1,值更新为 3

代码示例

以下是这段代码的逐步运行过程:

示例代码
counts = {}  # 空字典

# 第一次出现 "a"
element = "a"
counts[element] = counts.get(element, 0) + 1  # counts["a"] = 0 + 1
print(counts)  # 输出:{'a': 1}

# 第二次出现 "a"
counts[element] = counts.get(element, 0) + 1  # counts["a"] = 1 + 1
print(counts)  # 输出:{'a': 2}

# 第一次出现 "b"
element = "b"
counts[element] = counts.get(element, 0) + 1  # counts["b"] = 0 + 1
print(counts)  # 输出:{'a': 2, 'b': 1}

等价代码(用 if 实现)

counts[element] = counts.get(element, 0) + 1 是一种简洁写法。如果不用 get(),可以写成:

if element in counts:
    counts[element] += 1
else:
    counts[element] = 1

两种方式的效果是一样的,但 get() 的写法更加简洁。


适用场景

这段代码适用于统计某个集合(如列表、字符串)中每个元素的出现次数。例如:

示例:统计列表中每个数字出现的次数
data = [1, 2, 1, 3, 2, 1]
counts = {}

for element in data:
    counts[element] = counts.get(element, 0) + 1

print(counts)  # 输出:{1: 3, 2: 2, 3: 1}

更高级的实现:用 collections.Counter

如果需要统计出现次数,还可以使用 collections.Counter,它等效于手动构建字典的方法。

from collections import Counter

data = [1, 2, 1, 3, 2, 1]
counts = Counter(data)
print(counts)  # 输出:Counter({1: 3, 2: 2, 3: 1})

总结

  • counts[element] = counts.get(element, 0) + 1 的作用:
    • 如果 element 不存在,则初始化为 1
    • 如果 element 已经存在,则累加 1
  • 优点: 简洁、高效,无需显式检查键是否存在。
  • 等价于:
    if element in counts:
        counts[element] += 1
    else:
        counts[element] = 1
    
max_count = reduce(lambda x, y: x if x > y else y, counts.values(), 0)

如果不写0

报错的原因是 reduce() 的第二个参数,即 counts.values(),是一个空的可迭代对象。如果 reduce 没有初始值(initializer 参数),而传入的可迭代对象为空,reduce 无法执行,最终导致 TypeError。

可能出现的问题 counts 是一个空字典,这意味着在 for 循环中没有任何元素被添加到字典中。 原因在于 can_afford 列表是空的,filter 函数没有筛选出任何符合条件的元素。

reduce()

reduce 是 Python 中的一个高效工具,用于 对一个序列(列表、元组等)进行累积计算,直到得出一个最终结果。

它来自 functools 模块,所以使用前需要导入:

from functools import reduce

reduce 的语法

reduce(function, iterable, initializer=None)
  • function:一个接收两个参数的函数,用于定义如何累积计算。
  • iterable:要进行累积计算的序列。
  • initializer(可选):一个初始值。如果提供,计算从初始值和序列的第一个元素开始;否则直接从序列的前两个元素开始。

reduce 的核心工作原理

  1. 初始值:如果提供了 initializer,用它和序列的第一个元素进行计算;否则用序列的前两个元素。
  2. 累积计算:将上一次的结果与序列中的下一个元素作为输入,继续计算。
  3. 结果输出:直到序列的所有元素处理完,返回累积的最终结果。

reduce 的计算过程

示例代码
from functools import reduce

nums = [1, 2, 3, 4]
result = reduce(lambda x, y: x + y, nums)
print(result)  # 输出:10

计算步骤:

  1. 初始化序列 nums = [1, 2, 3, 4]
  2. 定义函数 lambda x, y: x + y,用于将两个值相加。
  3. 累积计算过程:
    • 第一次计算:x = 1, y = 2,结果:1 + 2 = 3
    • 第二次计算:x = 3(上次结果), y = 3(下一个元素),结果:3 + 3 = 6
    • 第三次计算:x = 6(上次结果), y = 4(下一个元素),结果:6 + 4 = 10

最终输出结果为 10


带初始值的 reduce

如果提供了 initializer,计算从 initializer 和序列的第一个元素开始。

示例代码
from functools import reduce

nums = [1, 2, 3, 4]
result = reduce(lambda x, y: x + y, nums, 10)
print(result)  # 输出:20

计算步骤:

  1. initializer = 10,序列 nums = [1, 2, 3, 4]
  2. 累积计算过程:
    • 第一次计算:x = 10(初始值), y = 1(第一个元素),结果:10 + 1 = 11
    • 第二次计算:x = 11(上次结果), y = 2(下一个元素),结果:11 + 2 = 13
    • 第三次计算:x = 13(上次结果), y = 3(下一个元素),结果:13 + 3 = 16
    • 第四次计算:x = 16(上次结果), y = 4(下一个元素),结果:16 + 4 = 20

最终输出结果为 20


reduce 的典型应用场景

1. 累积乘积

计算列表中所有元素的乘积:

nums = [1, 2, 3, 4]
product = reduce(lambda x, y: x * y, nums)
print(product)  # 输出:24
2. 字符串拼接

将列表中的字符串拼接成一个长字符串:

words = ["Hello", "World", "Python", "Reduce"]
result = reduce(lambda x, y: x + " " + y, words)
print(result)  # 输出:Hello World Python Reduce
3. 找最大值
nums = [5, 10, 3, 8]
max_value = reduce(lambda x, y: x if x > y else y, nums)
print(max_value)  # 输出:10
4. 从字典列表中提取特定值

假设有一个字典列表,求总金额:

transactions = [{"amount": 100}, {"amount": 200}, {"amount": 50}]
total = reduce(lambda x, y: x + y["amount"], transactions, 0)
print(total)  # 输出:350

reduce 与循环的关系

reduce 本质上等价于一个累积的 for 循环。用循环实现和 reduce 等效的代码如下:

示例代码
nums = [1, 2, 3, 4]

# 用 reduce 计算乘积
from functools import reduce
result = reduce(lambda x, y: x * y, nums)
print(result)  # 输出:24

# 用 for 循环计算乘积
product = 1
for num in nums:
    product *= num
print(product)  # 输出:24

reduce 的优缺点

优点:
  1. 简洁:适合用于简单的累积计算逻辑,避免显式写循环。
  2. 函数式编程风格:与 mapfilter 等组合使用,代码更加流畅。
缺点:
  1. 可读性较低:对于复杂逻辑,用 lambda 写的 reduce 不容易被初学者理解。
  2. 效率较低:在某些场景下,显式循环可能更高效。
  3. 替代性强:Python 的 for 循环完全可以替代 reduce,没有性能差异。

总结

  • 什么时候用 reduce

    • 当你需要对序列进行累积计算,而且逻辑简单(例如加法、乘法、最大值计算等)。
    • 需要写出紧凑的、函数式风格的代码时。
  • 什么时候不用 reduce

    • 如果逻辑较复杂,或者需要考虑代码的可读性,最好使用显式循环替代。

简单的累积操作可以用 reduce 优雅完成,但别滥用它!