小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
的值更新为新的次数。
逻辑总结
完整的逻辑可以分解为:
- 如果
element
不在counts
中,counts.get(element, 0)
返回0
,于是counts[element] = 0 + 1
,即将element
的值初始化为1
。 - 如果
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
的核心工作原理
- 初始值:如果提供了
initializer
,用它和序列的第一个元素进行计算;否则用序列的前两个元素。 - 累积计算:将上一次的结果与序列中的下一个元素作为输入,继续计算。
- 结果输出:直到序列的所有元素处理完,返回累积的最终结果。
reduce
的计算过程
示例代码
from functools import reduce
nums = [1, 2, 3, 4]
result = reduce(lambda x, y: x + y, nums)
print(result) # 输出:10
计算步骤:
- 初始化序列
nums = [1, 2, 3, 4]
。 - 定义函数
lambda x, y: x + y
,用于将两个值相加。 - 累积计算过程:
- 第一次计算:
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
计算步骤:
initializer = 10
,序列nums = [1, 2, 3, 4]
。- 累积计算过程:
- 第一次计算:
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
的优缺点
优点:
- 简洁:适合用于简单的累积计算逻辑,避免显式写循环。
- 函数式编程风格:与
map
、filter
等组合使用,代码更加流畅。
缺点:
- 可读性较低:对于复杂逻辑,用
lambda
写的reduce
不容易被初学者理解。 - 效率较低:在某些场景下,显式循环可能更高效。
- 替代性强:Python 的
for
循环完全可以替代reduce
,没有性能差异。
总结
-
什么时候用
reduce
?- 当你需要对序列进行累积计算,而且逻辑简单(例如加法、乘法、最大值计算等)。
- 需要写出紧凑的、函数式风格的代码时。
-
什么时候不用
reduce
?- 如果逻辑较复杂,或者需要考虑代码的可读性,最好使用显式循环替代。
简单的累积操作可以用 reduce
优雅完成,但别滥用它!