红包金额排行榜
问题描述
小C参与了一场抢红包的游戏,现在他想要对所有参与抢红包的人进行一次运气排名。
排名规则如下:抢到的金额越多,排名越靠前;如果两个人抢到的金额相同,
则按照他们抢红包的顺序进行排名。一个人可能不止抢一次红包
比如,如果小C和小U抢到的金额相同,但小C比小U先抢,则小C排在小U前面。
测试样例
样例1:
输入:n = 4 ,s = ["a", "b", "c", "d"] ,x = [1, 2, 2, 1] 输出:['b', 'c', 'a', 'd']
样例2:
输入:n = 3 ,s = ["x", "y", "z"] ,x = [100, 200, 200] 输出:['y', 'z', 'x']
样例3:
输入:n = 5 ,s = ["m", "n", "o", "p", "q"] ,x = [50, 50, 30, 30, 20] 输出:['m', 'n', 'o', 'p', 'q'] def solution(n: int, s: list, x: list) -> list: total_amount = {} # 创造空字典存放每个人名字s key和对应的金额x value for i in range(n): if s[i] not in total_amount.keys(): # 如果名字s[i]不在字典中 total_amount[s[i]] = 0 total_amount[s[i]] += x[i] # 把名字s[i]对应的金额x[i]加到字典中
# 这样每个人和他们的金额都被加进去了
participants = [(name, amount) for name, amount in total_amount.items()] # 把字典转化为列表
participants.sort(key = lambda x:(-x[1], s.index(x[0])), reverse = False) # 按照金额从大到小排序,如果金额相同按照名字从先到后排序
return [x[0] for x in participants]
if name == 'main': print(solution(4, ["a", "b", "c", "d"], [1, 2, 2, 1]) == ['b', 'c', 'a', 'd']) print(solution(3, ["x", "y", "z"], [100, 200, 200]) == ['y', 'z', 'x']) print(solution(5, ["m", "n", "o", "p", "q"], [50, 50, 30, 30, 20]) == ['m', 'n', 'o', 'p', 'q'])
lambda
匿名函数
1. 什么是 lambda
匿名函数?
lambda
是 Python 中创建简单函数的一种方式,和使用 def
定义的函数类似,但更紧凑。
-
普通函数 使用
def
定义,有名字:def add(x, y): return x + y print(add(3, 5)) # 输出:8
-
匿名函数 使用
lambda
定义,没有名字:add = lambda x, y: x + y print(add(3, 5)) # 输出:8
关键区别:
lambda
定义的函数通常用于 简单的逻辑,而def
可以定义复杂的逻辑。lambda
函数是单行的,返回值直接写在函数体中。
2. lambda
函数的语法
lambda 参数1, 参数2, ...: 表达式
- 参数部分:参数列表,与普通函数一样,可以有一个或多个参数。
- 表达式部分:单行逻辑,计算结果将作为函数的返回值。
例子:
# 定义一个 lambda 函数,实现两个数相加
add = lambda x, y: x + y
print(add(10, 20)) # 输出:30
3. 典型使用场景
lambda
通常用在需要一个 短小函数,而且 不需要重复使用 的场合。
(1) 作为 sorted
的 key
参数
在排序中,key
参数用于指定排序依据。lambda
函数经常用于动态指定排序规则。
例子:按元组的第二个元素排序
data = [(1, 3), (2, 2), (3, 1)]
# 按第二个元素排序
sorted_data = sorted(data, key=lambda x: x[1])
print(sorted_data) # 输出:[(3, 1), (2, 2), (1, 3)]
例子:先按分数排序,再按名字排序
students = [("Alice", 85), ("Bob", 90), ("Charlie", 85)]
sorted_students = sorted(students, key=lambda s: (-s[1], s[0]))
print(sorted_students)
# 输出:[('Bob', 90), ('Alice', 85), ('Charlie', 85)]
(2) 在 map
中使用
map
函数用于对列表中的每个元素应用一个函数。
例子:将每个数字平方
nums = [1, 2, 3, 4]
squared = map(lambda x: x**2, nums)
print(list(squared)) # 输出:[1, 4, 9, 16]
(3) 在 filter
中使用
filter
函数用于筛选出符合条件的元素。
例子:筛选出偶数
nums = [1, 2, 3, 4, 5, 6]
evens = filter(lambda x: x % 2 == 0, nums)
print(list(evens)) # 输出:[2, 4, 6]
(4) 在 reduce
中使用
reduce
函数(需要 functools
模块)用于对列表元素进行累积计算。
例子:计算列表所有元素的乘积
from functools import reduce
nums = [1, 2, 3, 4]
product = reduce(lambda x, y: x * y, nums)
print(product) # 输出:24
(5) 与 lambda
结合动态生成函数
例子:生成加法函数
def make_adder(n):
return lambda x: x + n
add_5 = make_adder(5)
print(add_5(10)) # 输出:15
4. 使用 lambda
的注意事项
-
只能写一行逻辑:
lambda
函数体只能写一个表达式,不能包含多行代码或复杂语句。
不能这么写:
lambda x: if x > 0: return x # 错误
可以这么写:
lambda x: x if x > 0 else 0
-
不要滥用
lambda
:- 如果逻辑复杂,或者需要多次复用函数,应该用
def
明确定义函数。
- 如果逻辑复杂,或者需要多次复用函数,应该用
5. 综合实例
(1) 多条件排序
employees = [("Alice", 30, 5000), ("Bob", 25, 6000), ("Charlie", 30, 4000)]
# 按年龄升序,工资降序排序
sorted_employees = sorted(employees, key=lambda e: (e[1], -e[2]))
print(sorted_employees)
# 输出:[('Bob', 25, 6000), ('Charlie', 30, 4000), ('Alice', 30, 5000)]
(2) 动态筛选数据
nums = [10, 15, 20, 25, 30]
# 筛选能被 5 整除的数
divisible_by_5 = filter(lambda x: x % 5 == 0, nums)
print(list(divisible_by_5)) # 输出:[10, 15, 20, 25, 30]
(3) 数据转换
# 将名字列表转换为大写
names = ["alice", "bob", "charlie"]
uppercase_names = map(lambda name: name.upper(), names)
print(list(uppercase_names)) # 输出:['ALICE', 'BOB', 'CHARLIE']
6. 总结
-
优点:
- 简洁,适合写短小的函数。
- 非常适合作为其他函数的参数,如
sorted
、map
、filter
。
-
缺点:
- 可读性较低,复杂逻辑会让代码难以理解。
- 只能处理简单逻辑,多行代码需要使用
def
函数。
推荐的使用方式:
- 当你只需要用一次短小函数,且逻辑清晰时,使用
lambda
是一个很好的选择。
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
优雅完成,但别滥用它!
s.index(p[0])
是在列表 s
中找到 p[0]
这个元素的索引位置(即它第一次出现的位置)。下面我详细解释:
分解含义
1. p
p
是 lambda p
中的参数,表示排序时当前处理的一个元素。这里,p
是 participants
列表中的每个元素。
2. p[0]
p
是一个元组,比如 (name, amount)
,它有两个部分:
p[0]
是元组的第一个值,即 名字。p[1]
是元组的第二个值,即 金额。
例如:
p = ("a", 1)
p[0] # 结果是 "a"
p[1] # 结果是 1
3. s.index(p[0])
s.index(x)
是 Python 列表的一个方法,用于 查找元素 x
在列表 s
中的索引位置。如果元素 x
在列表中多次出现,index()
返回第一次出现的位置。
在代码中,p[0]
是一个名字(比如 "a"
),s
是原始顺序的列表,比如 ["a", "b", "c", "d"]
。s.index(p[0])
就是找到名字在原始顺序中的位置。
代码实例
输入
participants = [("a", 1), ("b", 2), ("c", 2), ("d", 1)]
s = ["a", "b", "c", "d"]
计算 s.index(p[0])
我们逐一分析:
-
对于
p = ("a", 1)
:p[0] = "a"
s.index("a") = 0
-
对于
p = ("b", 2)
:p[0] = "b"
s.index("b") = 1
-
对于
p = ("c", 2)
:p[0] = "c"
s.index("c") = 2
-
对于
p = ("d", 1)
:p[0] = "d"
s.index("d") = 3
结果
p[0]
的索引在 s
中分别是:
s.index("a") = 0
s.index("b") = 1
s.index("c") = 2
s.index("d") = 3
在排序中的作用
在 key=lambda p: (-p[1], s.index(p[0]))
中,s.index(p[0])
是排序的 第二条件。
- 当两个人的金额相同时,
s.index(p[0])
用来比较他们在原始列表中的顺序。 - 比如:
- 如果金额相同,比如
("c", 2)
和("b", 2)
,则根据s.index(p[0])
:"b"
的索引是 1,排在"c"
(索引 2)之前。
- 如果金额相同,比如
完整示例
participants = [("a", 1), ("b", 2), ("c", 2), ("d", 1)]
s = ["a", "b", "c", "d"]
# 排序
sorted_participants = sorted(participants, key=lambda p: (-p[1], s.index(p[0])))
print(sorted_participants)
排序依据
("a", 1)
:(-1, s.index("a"))
→(-1, 0)
("b", 2)
:(-2, s.index("b"))
→(-2, 1)
("c", 2)
:(-2, s.index("c"))
→(-2, 2)
("d", 1)
:(-1, s.index("d"))
→(-1, 3)
排序结果
根据金额从大到小排序(-p[1]
),如果金额相同按原始顺序(s.index(p[0])
):
[("b", 2), ("c", 2), ("a", 1), ("d", 1)]
总结
s.index(p[0])
的作用是找到名字p[0]
在原始列表s
中的索引。- 在排序中,它确保当金额相同时,按原始顺序排序。
- 这是 多条件排序 的一种常见用法,用于保留原始顺序优先级。
index()
index()
函数
在 Python 中,index()
是列表 (list
) 和字符串 (str
) 对象的一个方法,用于查找 指定值第一次出现的索引位置。
基本语法
对于列表:
list.index(element, start=0, end=len(list))
element
:要查找的值。start
(可选):查找的起始位置,默认从列表的第一个元素开始。end
(可选):查找的结束位置,默认到列表的最后一个元素为止。
返回值:
- 如果找到,返回
element
的索引(从 0 开始)。 - 如果找不到,抛出
ValueError
。
对于字符串:
str.index(substring, start=0, end=len(string))
substring
:要查找的子字符串。start
和end
的含义与列表相同。
列表中的 index()
示例
(1) 查找元素的位置
my_list = ["a", "b", "c", "d"]
print(my_list.index("c")) # 输出:2
- 解释:
"c"
在列表中的索引是2
。
(2) 使用 start
和 end
可以指定一个范围,在该范围内查找元素。
my_list = ["a", "b", "c", "a", "b", "c"]
print(my_list.index("a", 2)) # 输出:3
print(my_list.index("b", 2, 5)) # 输出:4
- 解释:
my_list.index("a", 2)
从索引2
开始查找"a"
,找到索引为3
。my_list.index("b", 2, 5)
在索引范围[2, 5)
内查找"b"
,找到索引为4
。
(3) 查找不存在的元素
my_list = ["a", "b", "c"]
print(my_list.index("x")) # 抛出 ValueError: 'x' is not in list
- 解释: 如果元素不在列表中,
index()
会抛出ValueError
,需要用try...except
捕获异常。
字符串中的 index()
示例
(1) 查找子字符串的位置
text = "hello world"
print(text.index("world")) # 输出:6
- 解释: 子字符串
"world"
在索引6
开始。
(2) 使用 start
和 end
text = "hello world hello"
print(text.index("hello", 6)) # 输出:12
- 解释: 从索引
6
开始查找"hello"
,结果是索引12
。
(3) 查找不存在的子字符串
text = "hello world"
print(text.index("Python")) # 抛出 ValueError: substring not found
- 解释: 如果子字符串不存在,会抛出
ValueError
。
index()
的注意事项
-
大小写敏感
- 无论是列表还是字符串,
index()
都对大小写敏感。
text = "Hello World" print(text.index("hello")) # 抛出 ValueError
- 无论是列表还是字符串,
-
查找范围
- 可以通过
start
和end
参数限制查找范围,超出范围不会影响原始数据。
my_list = [1, 2, 3, 4, 5] print(my_list.index(3, 3)) # 抛出 ValueError,因为从索引 3 开始找不到 3
- 可以通过
-
效率
index()
的时间复杂度是O(n)
,因为它需要逐个元素遍历列表或字符串直到找到目标值。
结合实际案例
(1) 多条件排序时使用 index
在排序时,通过 index()
保证按原始顺序排序:
s = ["a", "b", "c", "d"]
participants = [("a", 1), ("b", 2), ("c", 2), ("d", 1)]
sorted_participants = sorted(participants, key=lambda p: (-p[1], s.index(p[0])))
print(sorted_participants)
输出:
[('b', 2), ('c', 2), ('a', 1), ('d', 1)]
s.index(p[0])
的作用: 根据名字在原始列表s
中的索引排序。
(2) 筛选字符串中的关键词
用 index()
查找多个关键词在字符串中的位置:
text = "This is a simple example"
keywords = ["simple", "example"]
positions = {kw: text.index(kw) for kw in keywords}
print(positions)
输出:
{'simple': 10, 'example': 17}
index()
与 find()
的区别(针对字符串)
-
返回值
index()
:如果找不到目标值,抛出ValueError
。find()
:如果找不到目标值,返回-1
。
示例:
text = "hello world" print(text.index("Python")) # 抛出 ValueError print(text.find("Python")) # 输出:-1
-
功能
- 如果你想要查找并处理找不到目标值的情况,
find()
更安全。 - 如果你确信目标值一定存在,用
index()
更直接。
- 如果你想要查找并处理找不到目标值的情况,
总结
index()
的作用: 查找元素或子字符串在列表或字符串中第一次出现的索引。- 适用场景:
- 列表中查找元素的位置。
- 字符串中查找子字符串的位置。
- 排序时,利用原始顺序保证稳定性。
- 关键点:
- 如果找不到目标值,
index()
会抛出ValueError
。 - 如果需要更安全的查找方式,可以用
find()
(针对字符串)。
- 如果找不到目标值,