红包金额排行榜 | 豆包MarsCode AI刷题

4 阅读14分钟

红包金额排行榜

问题描述

小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) 作为 sortedkey 参数

在排序中,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 的注意事项

  1. 只能写一行逻辑

    • lambda 函数体只能写一个表达式,不能包含多行代码或复杂语句。

    不能这么写:

    lambda x: if x > 0: return x  # 错误
    

    可以这么写:

    lambda x: x if x > 0 else 0
    
  2. 不要滥用 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. 总结

  • 优点

    • 简洁,适合写短小的函数。
    • 非常适合作为其他函数的参数,如 sortedmapfilter
  • 缺点

    • 可读性较低,复杂逻辑会让代码难以理解。
    • 只能处理简单逻辑,多行代码需要使用 def 函数。

推荐的使用方式:

  • 当你只需要用一次短小函数,且逻辑清晰时,使用 lambda 是一个很好的选择。

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 优雅完成,但别滥用它!

s.index(p[0]) 是在列表 s 中找到 p[0] 这个元素的索引位置(即它第一次出现的位置)。下面我详细解释:


分解含义

1. p

plambda p 中的参数,表示排序时当前处理的一个元素。这里,pparticipants 列表中的每个元素。

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)
排序依据
  1. ("a", 1)(-1, s.index("a"))(-1, 0)
  2. ("b", 2)(-2, s.index("b"))(-2, 1)
  3. ("c", 2)(-2, s.index("c"))(-2, 2)
  4. ("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:要查找的子字符串。
  • startend 的含义与列表相同。

列表中的 index() 示例

(1) 查找元素的位置
my_list = ["a", "b", "c", "d"]
print(my_list.index("c"))  # 输出:2
  • 解释: "c" 在列表中的索引是 2
(2) 使用 startend

可以指定一个范围,在该范围内查找元素。

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) 使用 startend
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() 的注意事项

  1. 大小写敏感

    • 无论是列表还是字符串,index() 都对大小写敏感。
    text = "Hello World"
    print(text.index("hello"))  # 抛出 ValueError
    
  2. 查找范围

    • 可以通过 startend 参数限制查找范围,超出范围不会影响原始数据。
    my_list = [1, 2, 3, 4, 5]
    print(my_list.index(3, 3))  # 抛出 ValueError,因为从索引 3 开始找不到 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() 的区别(针对字符串)

  1. 返回值

    • index():如果找不到目标值,抛出 ValueError
    • find():如果找不到目标值,返回 -1

    示例:

    text = "hello world"
    print(text.index("Python"))  # 抛出 ValueError
    print(text.find("Python"))  # 输出:-1
    
  2. 功能

    • 如果你想要查找并处理找不到目标值的情况,find() 更安全。
    • 如果你确信目标值一定存在,用 index() 更直接。

总结

  • index() 的作用: 查找元素或子字符串在列表或字符串中第一次出现的索引。
  • 适用场景:
    • 列表中查找元素的位置。
    • 字符串中查找子字符串的位置。
    • 排序时,利用原始顺序保证稳定性。
  • 关键点:
    • 如果找不到目标值,index() 会抛出 ValueError
    • 如果需要更安全的查找方式,可以用 find()(针对字符串)。