题目地址(911. 在线选举)
https://leetcode-cn.com/problems/online-election/
题目描述
在选举中,第 i 张票是在时间为 times[i] 时投给 persons[i] 的。
现在,我们想要实现下面的查询函数: TopVotedCandidate.q(int t) 将返回在 t 时刻主导选举的候选人的编号。
在 t 时刻投出的选票也将被计入我们的查询之中。在平局的情况下,最近获得投票的候选人将会获胜。
示例:
输入:["TopVotedCandidate","q","q","q","q","q","q"], [[[0,1,1,0,0,1,0],[0,5,10,15,20,25,30]],[3],[12],[25],[15],[24],[8]]
输出:[null,0,1,1,0,0,1]
解释:
时间为 3,票数分布情况是 [0],编号为 0 的候选人领先。
时间为 12,票数分布情况是 [0,1,1],编号为 1 的候选人领先。
时间为 25,票数分布情况是 [0,1,1,0,0,1],编号为 1 的候选人领先(因为最近的投票结果是平局)。
在时间 15、24 和 8 处继续执行 3 个查询。
提示:
1 <= persons.length = times.length <= 5000
0 <= persons[i] <= persons.length
times 是严格递增的数组,所有元素都在 [0, 10^9] 范围中。
每个测试用例最多调用 10000 次 TopVotedCandidate.q。
TopVotedCandidate.q(int t) 被调用时总是满足 t >= times[0]。
前置知识
公司
思路
题目给了一个 times 数组, 我们可以记录 times 中每一时刻 t 的优胜者,只需要边遍历边统计票数,用两个全局参数 max_voted_person 和 max_voted_count 分别表示当前票数最多的人和其对应的票数即可。
由于题目要求如果票数相同取最近的,那么只需要在更新 max_voted_person 和 max_voted_count 的时候,增加如果当前人票数和 max_voted_count 一致也更新 max_voted_person 和 max_voted_count逻辑即可轻松实现。
由于题目没有说 person[i] 是 [0, N) 区间的值,使用数组统计不方便,因此这里我使用哈希表进行统计。
核心代码:
class TopVotedCandidate:
def __init__(self, persons: List[int], times: List[int]):
vote_count = collections.defaultdict(int) # 哈希表统计每个人的票数信息
max_voted_person = -1
max_voted_count = 0
winner = []
# zip([1,2,3], [4,5,6]) 会返回 [[1,4], [2,5], [3,6]]
for p, t in zip(persons, times):
vote_count[p] += 1
if vote_count[p] >= max_voted_count:
max_voted_count = vote_count[p]
max_voted_person = p
# 更新 winner
winner.append(max_voted_person)
经过上面的处理生成了一个 winner 数组,winner 数组和 times 以及 persons 是等长的。
接下来就是查询了,查询的 api 如下:
我们要做的就是使用 t 去前面生成好的 winner 数组找。由于 times 是有序的,因此查询过程我们就可以使用二分了。
比如:
times = [2,4,5,6]
winner = [1,2,1,1]
表示的就是:
如果 t 为 2, 4, 5, 6 我们直接返回 winner 对应的项目即可。比如 t 为 2,2 在 times 中 第 0 项,因此返回 winner[0]即可。
如果 t 为 3 呢?3 不在 [2,4,5,6] 中。根据题目要求,我们需要以 3 的最近的一个往前的时间点,也就是 2 ,我们仍然需要返回 winner[0]。
总的来说,其实我们需要找的位置就是一个最左插入位置,即将 t 插入 times 之后仍然保持有序的位置。比如 t 为 3 就是 [2,3,4,5,6],我们需要返回 3 的前一个。关于最左插入我在二分查找 进行了详细的描述,不懂的可以看下。
关键点解析
使用哈希表记录 times 中每一个时刻的优胜信息
代码
代码支持: Python3
class TopVotedCandidate:
def __init__(self, persons: List[int], times: List[int]):
vote_count = collections.defaultdict(int)
max_voted_person = -1
max_voted_count = 0
winner = []
for p, t in zip(persons, times):
vote_count[p] += 1
if vote_count[p] >= max_voted_count:
max_voted_count = vote_count[p]
max_voted_person = p
winner.append(max_voted_person)
self.winner = winner
self.times = times
def q(self, t: int) -> int:
winner = self.winner
# times 是不重复的,也就是严格递增的,类似 [2,4,5,6],这是关键
# eg:
# times [2,4,5,6]
# winner [1,2,1,1]
i = bisect.bisect_left(self.times, t)
if i != len(self.times) and self.times[i] == t:
return winner[i]
return winner[i - 1]
复杂度分析
时间复杂度:初始化的时间复杂度为 $O(N)$,q 的复杂度为 $O(logN)$,其中 N 为数组长度。
空间复杂度:我们使用了 vote_count 记录投票情况,因此空间复杂度 $O(N)$,其中 N 为数组长度。