0911. 在线选举

题目地址(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 如下:

q(int t) -> int

我们要做的就是使用 t 去前面生成好的 winner 数组找。由于 times 是有序的,因此查询过程我们就可以使用二分了。

比如:

times =  [2,4,5,6]
winner = [1,2,1,1]

表示的就是:

  • 2,5,6 时刻的优胜者是 1

  • 4 时刻优胜者是 2

如果 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 为数组长度。

最后更新于