leetcode:在线选举问题

题目来源:力扣

题目描述:

在选举中,第 i 张票是在时间为 times[i] 时投给 persons[i] 的。
现在,我们想要实现下面的查询函数: TopVotedCandidate.q(int t) 将返回在 t 时刻主导选举的候选人的编号。
在 t 时刻投出的选票也将被计入我们的查询之中。在平局的情况下,最近获得投票的候选人将会获胜。
最多5000候选人.

审题:

此题可分解为两个子问题:

  1. 记录每一次投票后的领先候选人
  2. 搜索距离给定时刻t最近的投票时刻 t ′ t' t, t ′ < t t'<t t<t

求解方案:
对于问题1, 我们可以使用优先队列纪录维护所有候选人的得票结果,优先队列可以在O(lgN)时间复杂度内返回当前领先的侯选人.

对于问题2,由于time数组为递增数组, 因此可以使用二分搜索搜索t时刻对应的投票时刻.

基于优先队列的java实现

class TopVotedCandidate {

    //创建Person类维护候选人的编号, 当前得票,以及最近投票时刻
    class Person{
        int id;
        int count;
        int recentVoteTime;
        public Person(int id, int count, int recentVoteTime){
            this.id = id;
            this.count = count;
            this.recentVoteTime = recentVoteTime;
        }
    }

	//创建候选人的比较器用于优先队列中候选人大小比较
    class PersonCmp implements Comparator<Person>{
        public int compare(Person p1, Person p2){ 
            //由于优先队列中最小元素优先级最高,因此此处得票多的候选人小于得票少的候选人, 
            // 同等得票时, 投票时间更晚的人小于更早的人
            if(p1.count != p2.count){
                return Integer.compare(p2.count, p1.count);
            }
            else
                return Integer.compare(p2.recentVoteTime, p1.recentVoteTime);
            
        }
    }

    int[] winId; //纪录投票更新后领先的候选人ID
    int[] voteTime; //复制投票时刻

    public TopVotedCandidate(int[] persons, int[] times) {
        winId = new int[times.length];
        Person[] IndexedPersons = new Person[5000]; //最多5000候选人
        voteTime = new int[times.length];
        PriorityQueue<Person> pq = new PriorityQueue<>(10, new PersonCmp()); //创建优先队列
        for(int i = 0; i < persons.length; i++){
            //更新候选人得票
            int id = persons[i];
            if(IndexedPersons[id] == null){
                IndexedPersons[id] = new Person(id, 1, times[i]);
            }
            else{
                pq.remove(IndexedPersons[id]);
                IndexedPersons[id] = new Person(id, IndexedPersons[id].count+1, times[i]);
            }
            pq.offer(IndexedPersons[id]);
            winId[i] = pq.peek().id;   
        }
        System.arraycopy(times, 0, voteTime, 0, times.length); //复制投票时间数组
    }
    
    public int q(int t) {
        //二分搜索t对应的最近投票时刻
        int lo = 0;
        int hi = voteTime.length;
        int mid;
        while(lo < hi){
            mid = lo + (hi-lo)/2;
            if(voteTime[mid] == t) 
                return winId[mid];
            else if(voteTime[mid] > t)//如果voteTime[mid] > t, 则最近投票时刻一定在[lo, mid)范围中
                hi = mid;
            else //最近投票时刻可能在[mid+1, hi)中,也可能等于mid
                lo = mid+1;
        }
        //如果未搜索到,则投票时刻为lo-1
        return winId[lo-1];
    }
}

在看了其他人写的题解后,发现这道题使用优先队列还是有点复杂了,可谓杀鸡用牛刀.由于候选人票数仅会增加,因此可以在遍历过程中纪录领先候选人的得票数,如果当前投票足以改变领先候选人,则更新,否则更新得票数即可.这样一来,每次投票后搜索领先候选人的时间复杂度仅为O(1), 而非O(lgN), 因此不仅实现简单, 算法效率从理论上也更高了.

java实现:
class TopVotedCandidate {


    int[] winId; //纪录投票更新后领先的候选人ID
    int[] voteTime;

    public TopVotedCandidate(int[] persons, int[] times) {
        winId = new int[times.length];
        voteTime = new int[times.length];
        int[] personVote = new int[5000]; //最多5000个候选人.
        //初始化
        int maxCount = 1;
        int maxId = persons[0];
        personVote[maxId] = maxCount;
        for(int i = 1; i < persons.length; i++){
            personVote[persons[i]]++; //得票+1;
            if(personVote[persons[i]] >= maxCount){
                maxCount = personVote[persons[i]];
                maxId = persons[i]; //更新maxId
            }
            winId[i] = maxId;
        }
        System.arraycopy(times, 0, voteTime, 0, times.length); //复制投票时间数组
    }
    
    public int q(int t) {
        //二分搜索t对应的最近投票时刻
        int lo = 0;
        int hi = voteTime.length;
        int mid;
        while(lo < hi){
            mid = lo + (hi-lo)/2;
            if(voteTime[mid] == t)
                return winId[mid];
            else if(voteTime[mid] > t)
                hi = mid;
            else
                lo = mid+1;
        }
        return winId[lo-1];
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值