【力扣时间】【911】【中等】在线选举

周六!放松!共勉!

今天的题目编号挺特殊的

1、先来看题

我是题目

今天的每日一题,中等难度。题目稍微有些绕,但理清重点后还是很好理解。

我对自己做中等题目的要求是,5分钟内能有明确的思路。
不过今天挺幸运的,也可能是接触过较多类似的题,在读完题后就有了完整的思路。

2、审题

今天的题目只写了一个case,而却罗里吧嗦地列了一长串解释,但重点却寥寥无几。

简单总结一下:

  1. 编写一个类,其构造函数传入persons[]和times[]。q(int t)方法实现每次调用时,返回t时刻的胜选人。
  2. time[]严格递增,在time[i]时刻的票投给了persons[i],一次仅投1票。
  3. 0 <= persons[i] < persons.length,意味着候选人的数量不可控。
  4. 在平局的情况下,最近获得投票的候选人将会获胜。
  5. 询问(调用q())时的t,不一定出现在time[]数组中(做过的朋友会发现,甚至有超出了time[]中的最大值的)
  6. 测试用例会重复调用多次q(),最多调用 104 次 。

3、思路

依旧没有陷阱(我还真是做脑筋急转弯做怕了 )。
辣么来说下自己的思路吧。

由于候选人是复数个的,于是我们必须记录每次投票后,投给的候选人的总票数。
好在每次投票仅投给一个人,这样会让每次投票的数据变化很好掌控。我们以Map来记录每个候选人的得票数。
由于time[]严格递增,所以我们不用分别记录某个时刻的得票数,仅以一个Map全局记录便可。

由于q()会调用多次,且每次传入的t不一定是time[]中的数据,所以我决定用一个数组来记录所有时刻的胜选者。

好了,胜利的方程式已经确认,开工吧!

4、撸代码

class TopVotedCandidate {

    private int maxTime;

    private int[] question;

    public TopVotedCandidate(int[] persons, int[] times) {
        //记录最大时间
        maxTime = times[times.length - 1];

        //初始化数组
        question = new int[maxTime + 1];
        Arrays.fill(question,-1);

        Map<Integer, Integer> votes = new HashMap<>();
        int maxVote = 0;

        for (int i = 0; i < times.length; i++) {
            //i时刻投给了persons[i]
            int person = persons[i];

            //i时刻,person的得票数
            int vote = votes.getOrDefault(person, 0) + 1;

            //票数反超时
            if (vote >= maxVote) {
                maxVote = vote;
                //记录此时的胜者
                question[times[i]] = person;
            }

            votes.put(person, vote);
        }

        int prevWinner = 0;
        for (int t = 0; t < question.length; t++) {
            if (question[t] != -1) {
                prevWinner = question[t];
            } else {
                question[t] = prevWinner;
            }
        }
    }

    public int q(int t) {
        //当询问时间超过最大时间时,返回栈的最后一个记录
        if (t > maxTime) {
            return question[question.length - 1];
        }
        return question[t];
    }
}

整体实现确实是顺风顺水,不过额外有些case让我大跌眼镜,所以中途还是改过几版。
这是我第一次成功提交的版本。保留了完整的思路和注释。

5、解读

就如同我在思路中写到的,votes是一个Map,我用它记录了所有候选者的得票数。
在遍历time[]时,以maxVote记录当前时间的最高得票数。
并在胜选者发生变化时,在question数组的当前时间下记录了候选者的信息。

question[times[i]] = person

注意,由于平票时,最近得票的候选者胜出。所以判断条件为

if (vote >= maxVote)

之后的核心要点就是

int prevWinner = 0;
for (int t = 0; t < question.length; t++) {
    if (question[t] != -1) {
        prevWinner = question[t];
    } else {
        question[t] = prevWinner;
    }
}

由于q()方法会调用多次,于是我决定用question数组记录下每个时刻的胜选人。
这样在调用q()方法时,就能直接从对应数组下标取出当前的胜选者,从而让此方法的时间复杂度变为O(1)

 public int q(int t) {
        //当询问时间超过最大时间时,返回栈的最后一个记录
        if (t > maxTime) {
            return question[question.length - 1];
        }
        return question[t];
    }

这里多了一个判断,是我在提交后发现case中有出现t超过times[]中的最大值的情况。
于是我用一个全局变量maxTime记录了times[]的最大值。并在t超过maxTime时,直接返回question[]的最后一位。

6、提交

在这里插入图片描述
时间排名接近100%。
由于我是以空间换时间,所以在内存上的消耗会较大笑死,我才不在意空间复杂度呢 。不过75%的排名比我预期的要高挺多。

7、咀嚼

初始化时的时间复杂度为O(N+M),N为times[]的长度,M为question[]的长度,也就是times[]中的最大值。
而查询时的时间复杂度为O(1)

之后我自然查看了官解和一些其他大牛的解法。
由于初始化时,官解并没有初始化question[],所以时间复杂度会小一些,为O(N)
而代价是调用q(t)时使用二分查找最大的<=t的一个时间节点,复杂度为O(logn)

原来提示里的二分查找是用在这的啊

8、他人的智慧

惯例,来学习下他人的解法

不过,这种解法我也会就是了。果然今天的应该算是简单题吗?

9、总结

由于今天的中等题很简单,收获可能并不明显。
不过经常刷题还是得明确目标:
中等题5分钟之类出思路!

本人也在面试中被面试官出过多次算法题(字节尤其喜欢出算法题 ),个人觉得面试官出题的难度大多集中在中等题难度。
所以中等题也是我个人重点的考察对象。

最后放一张我老婆的照片。2022年怎么还不来啊!

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值