摩尔投票(绝对众数)
绝对众数:数组内出现次数大于 的数。
求绝对众数的方法:
暴力做法 排序并枚举左端点。
摩尔投票: 求出。
摩尔投票
丢个模板。
int now = -1; int cnt = 0;
f(j, 1, n) {
if(x[j] != now) cnt--;
else cnt++;
if(cnt < 0) {
now = x[j]; cntx = 1;
}
}
int ccnt = 0;
f(j, 1, n) {
if(x[j] == now) ccnt++;
}
if (ccnt >= len){
cout << cnt << endl;
}
注意,序列存在区间众数时,一定是 。序列不存在区间众数时, 随机。所以还要再扫一遍。
拓展到 n / k
https://leetcode.cn/problems/majority-element-ii/
可以证明,出现次数超过 的数最多只有 个。否则必然违背「数总共只有 个」或者「当前统计的是出现次数超过 的数」的前提条件。
当明确了符合要求的数的数量之后,我们可以使用有限变量来代表这 个候选数及其出现次数。
然后使用「摩尔投票」的标准做法,在遍历数组时同时 check 这 个数,假设当前遍历到的元素为 :
- 如果 本身是候选者的话,则对其出现次数加一;
- 如果 本身不是候选者,检查是否有候选者的出现次数为 :
若有,则让 代替其成为候选者,并记录出现次数为 ;
若无,则让所有候选者的出现次数减一。
当处理完整个数组后,这 个数可能会被填满,但不一定都是符合出现次数超过 要求的。
需要进行二次遍历,来确定候选者是否符合要求,将符合要求的数加到答案。
上述做法正确性的关键是:若存在出现次数超过 的数,最后必然会成为这 个候选者之一。
我们可以通过「反证法」来进行证明:若出现次数超过 的数 最终没有成为候选者。
有两种可能会导致这个结果:
数值 从来没成为过候选者:
如果 从来没成为过候选者,那么在遍历 的过程中,必然有 个候选者被减了超过 次,假设当前 出现次数为 ,已知 ,此时总个数为
再根据 ,可知 ,而我们总共就只有 个数,因此该情况恒不成立。
数值 成为过候选者,但被逐出替换了:
同理,被逐出替换,说明发生了对 出现次数减一的动作(减到 ),每次的减一操作,意味着有其余的 个候选者的出现次数也发生了减一动作,加上本身被遍历到的当前数 ,共有 个数字的和 被一同统计。
因此,根据我们摩尔投票的处理过程,如果 成为过候选者,并被逐出替换,那么同样能够推导出我们存在超过 个数。
综上,如果存在出现次数超过 的数,其必然会成为 个候选者之一。
这个算法的时间复杂度为 。同时暴力的 保持不变,不失为一种可能的做法。
放在线段树内维护
需要指出的是,摩尔投票可以表示为一个二元组,是可以 合并的信息,并且具有结合律。可以在线段树内维护。
更深刻的用法和性质
考虑区间众数的两个求法。
-
不确定候选众数。可以摩尔投票,得出最大数并且 确定 ,发现它是绝对众数。这个方法不可差分。
-
确定候选众数 。每个数,如果是 看成 ,否则看成 。那么任意一个区间有绝对众数 当且仅当区间和 。这个可以差分为若干个前缀和。
对于第二种,我们有性质:任何一个区间只有一个绝对众数,所以如果对所有候选众数做一遍,那么得到的所有有众数的区间不重复。
还有两个深刻的性质:
-
区间 有绝对众数 ,那么其分裂成任意两个区间 以及 ,这两个区间满足一定存在其中一个有绝对众数 。
-
这些区间,最多有 个不同的众数。
因为某个数要做绝对众数,一定比之前的所有数都多。于是出现次数 。
这两个性质组合起来可以有如下性质:
跨过 的,在 内的所有区间的绝对众数最多有 种。这正是 cdq 分治的标准处理形式!
具体地,考虑 和 中所有出现过的绝对众数,就是这些区间可能有的所有绝对众数。
然后你要批量处理这些区间的信息,可以这样:对于每一个可能的绝对众数,做第 2 种判定方式(前缀和),得到 。然后分成两半 和 ,排序然后双指针归并, 有绝对众数 当且仅当 。
这样处理起来,时间复杂度:
是三个 的,但是卡的非常不满,跑起来像一个 一样(笑,排序我可以改成桶,然后绝对众数卡不满这样就像 了)
5e5 跑了 600ms(arc159_f)。
总结一下,这个方法帮助我们找到和处理所有含有绝对众数 的区间的方式,是很万能的,但是时间复杂度稍微难看点。
考虑最后一个过程,是给定一些 ,查询 的问题。这是一维偏序,值域是 的,你可以认为 是查询,查询 的数有几个。可以 预处理答案,对于每一个 都是询问一下的事情。这样直接降到了 。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现