红包运气排行榜 | 豆包MarsCode AI刷题

307 阅读5分钟

红包运气排行榜

问题描述

小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']

算法设计

  1. 创建一个新的数据结构 Person 来存储每个人的姓名和得分。
  2. 初始化一个空的 persons 切片来存储所有人的信息。
  3. 遍历输入的姓名和得分列表,对于每一个姓名,检查是否已经在 persons 中存在。如果存在,更新对应人员的得分;如果不存在,添加新的人员记录。
  4. 使用 sort.Slice 函数对 persons 进行排序。首先比较得分,得分相同时再比较他们在原始列表中的位置。
  5. 将排好序的人员名单转换回字符串列表并返回。
    1. 简单地以得分进行排序,会在提交代码时产生解答错误,原因是在测试用例中会出现同一个名称多次得分的情况,需要将得分相加,再进行排序,如测试用例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"]
  1. 部分排序算法可能会导致抢到金额相同的两个人的排名顺序颠倒(如冒泡排序),需使用合适的排序算法进行解答(作者这里使用的是插入排序)

时间复杂度

初始化

  • 时间复杂度:初始化 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
}

优化

  1. 使用哈希表:为了提高查找效率,可以使用哈希表来存储每个人的累计金额和索引。这样可以在常数时间内完成查找和更新操作。
  2. 减少不必要的遍历:在更新 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)。