红包运气排行榜
问题描述
小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']
算法设计
- 创建一个新的数据结构 Person 来存储每个人的姓名和得分。
- 初始化一个空的 persons 切片来存储所有人的信息。
- 遍历输入的姓名和得分列表,对于每一个姓名,检查是否已经在 persons 中存在。如果存在,更新对应人员的得分;如果不存在,添加新的人员记录。
- 使用 sort.Slice 函数对 persons 进行排序。首先比较得分,得分相同时再比较他们在原始列表中的位置。
- 将排好序的人员名单转换回字符串列表并返回。
- 注
- 简单地以得分进行排序,会在提交代码时产生解答错误,原因是在测试用例中会出现同一个名称多次得分的情况,需要将得分相加,再进行排序,如测试用例4:
输入:n = 12 , s = ["aa","aaaaaaa","aaaa","aaaa","aaaa","aaaaaaaaaa","aaaaaaaaa","aaaa","aaaaaaaaaa","aaaaaaaaa","aaaaa","aaaa"] ,x = [17,14,11,2,8,16,14,17,10,6,5,12]
输出:["aaaa","aaaaaaaaaa","aaaaaaaaa","aa","aaaaaaa","aaaaa"]
- 部分排序算法可能会导致抢到金额相同的两个人的排名顺序颠倒(如冒泡排序),需使用合适的排序算法进行解答(作者这里使用的是插入排序)
时间复杂度
初始化
- 时间复杂度:初始化
persons切片的时间复杂度为 O(1)。
数据处理
- 遍历输入列表:遍历输入的姓名和金额列表,时间复杂度为 O(n),其中 n 是输入列表的长度。
- 查找和更新:
- 对于每个姓名,需要在
persons切片中查找是否存在该姓名,最坏情况下需要遍历整个persons切片,时间复杂度为 O(k),其中 k 是persons切片的长度。 - 更新或添加新的
Person对象的时间复杂度为 O(1)。
- 对于每个姓名,需要在
因此,数据处理的总时间复杂度为 O(n · k)。
排序
- 排序操作:对
persons切片进行排序,时间复杂度为 O(k log k)。
结果提取
- 遍历排序后的列表:提取排序后的姓名,时间复杂度为 O(k)。
总体时间复杂度
- 数据处理:O(n · k)
- 排序:O(k \log k)
- 结果提取:O(k)
总体时间复杂度为 O(n · k + k log k)。
空间复杂度分析
- persons 切片:存储每个人的累计金额和姓名,空间复杂度为 O(k)。
- 结果列表:存储排序后的姓名,空间复杂度为 O(k)。
总体空间复杂度为 O(k)。
代码
package main
import (
"fmt"
"sort"
)
type Person struct {
Name string
Score int
}
func solution(n int, s []string, x []int) []string {
persons := make([]Person, 0, n)
for i := range s {
found := false
for j, person := range persons {
if person.Name == s[i] {
person.Score += x[i]
persons[j] = person
found = true
break
}
}
if !found {
persons = append(persons, Person{Name: s[i], Score: x[i]})
}
}
sort.Slice(persons, func(i, j int) bool {
if persons[i].Score == persons[j].Score {
return i < j
}
return persons[i].Score > persons[j].Score
})
result := make([]string, len(persons))
for i, person := range persons {
result[i] = person.Name
}
return result
}
测试
func main() {
fmt.Println(sliceEqual(solution(4, []string{"a", "b", "c", "d"}, []int{1, 2, 2, 1}), []string{"b", "c", "a", "d"}))
fmt.Println(sliceEqual(solution(3, []string{"x", "y", "z"}, []int{100, 200, 200}), []string{"y", "z", "x"}))
fmt.Println(sliceEqual(solution(5, []string{"m", "n", "o", "p", "q"}, []int{50, 50, 30, 30, 20}), []string{"m", "n", "o", "p", "q"}))
}
func sliceEqual(a, b []string) bool {
if len(a) != len(b) {
return false
}
for i, v := range a {
if v != b[i] {
return false
}
}
return true
}
优化
- 使用哈希表:为了提高查找效率,可以使用哈希表来存储每个人的累计金额和索引。这样可以在常数时间内完成查找和更新操作。
- 减少不必要的遍历:在更新
persons切片时,可以使用哈希表来避免重复遍历。
优化后的代码
package main
import (
"fmt"
"sort"
)
type Person struct {
Name string
Score int
Index int
}
func solution(n int, s []string, x []int) []string {
// 使用哈希表来存储每个人的累计金额和索引
personMap := make(map[string]Person)
for i := 0; i < n; i++ {
if person, ok := personMap[s[i]]; ok {
person.Score += x[i]
personMap[s[i]] = person
} else {
personMap[s[i]] = Person{Name: s[i], Score: x[i], Index: i}
}
}
// 将哈希表中的数据转换为切片
persons := make([]Person, 0, len(personMap))
for _, person := range personMap {
persons = append(persons, person)
}
// 排序
sort.Slice(persons, func(i, j int) bool {
if persons[i].Score == persons[j].Score {
return persons[i].Index < persons[j].Index
}
return persons[i].Score > persons[j].Score
})
// 提取结果
result := make([]string, len(persons))
for i, person := range persons {
result[i] = person.Name
}
return result
}
func main() {
fmt.Println(solution(4, []string{"a", "b", "c", "d"}, []int{1, 2, 2, 1}))
fmt.Println(solution(3, []string{"x", "y", "z"}, []int{100, 200, 200}))
fmt.Println(solution(5, []string{"m", "n", "o", "p", "q"}, []int{50, 50, 30, 30, 20}))
}
优化后的时间复杂度
- 数据处理:使用哈希表查找和更新的时间复杂度为 O(n)。
- 排序:对
persons切片进行排序的时间复杂度为 O(k log k)。 - 结果提取:提取排序后的姓名的时间复杂度为 O(k)。
总体时间复杂度为 O(n + k log k)。