题目链接:P2397
题意
非常简单,就是求众数,并且保证众数出现的频率大于0.5。
思路
这个题目还是挺有意思的,有很多种思想可以解决,这里写几种我知道的
1. 排序+统计
通过排序可以使相同的数下标相邻,然后扫一遍就可以统计出众数了
时间复杂度根据排序的时间复杂度决定。应是
Θ
(
n
log
n
)
\Theta(n \log n)
Θ(nlogn) (应该并不能用桶排。。)
但是这题的
n
n
n 可以到2e6,这种方法还不够优秀
此外原题的空间限制实际上不够我们存下数组,严格来说这种方法就不可能通过
2. 抽样调查
如果说方法1是全面调查,那么我们自然会有抽样调查的方法——随机抽取下标,选出一部分数算出众数作为答案。
显然这个算法的复杂度是可以不超的。而bzy神仙告诉我们,在
n
n
n 很大的时候,这种方法也有着很低的错误率,因此这种方法可行。
实现的时候可以抽
1
20
\dfrac1{20}
201 左右的数。但是
n
n
n 太小的时候它会被
h
a
c
k
hack
hack 。个人认为抽
min
(
100000
,
n
)
\min(100000,n)
min(100000,n) 个比较稳一些(当然方法不唯一
时间复杂度 Θ ( m log m ) \Theta(m\log m) Θ(mlogm) ,其中 m m m 为样本容量
3. 摩尔投票法
这应该是最妙的一种方法了。时间
Θ
(
n
)
\Theta(n)
Θ(n) ,空间
Θ
(
1
)
\Theta(1)
Θ(1) 。
它的思想是,让不同的数两两抵消,最后剩下来的就是众数。
我们可以看一下这个命题:一个数列中众数出现的频率过半,那么从中去掉两个不同的数,众数不会改变。
这个命题显然正确,是真的显然。不信你自己想想qwq
那么根据这个命题,只要模拟一个两两抵消的过程,我们就可以得到结果。
具体操作起来是这样的
先设定一个当前数
x
x
x ,并存储它出现的次数
c
n
t
cnt
cnt (初值为0)。
假设现在读进来的数为
y
y
y
当
x
≠
y
x \ne y
x=y 时,显然可以两两抵消。如果
c
n
t
=
0
cnt=0
cnt=0 ,说明
x
x
x 实际上已经没了,那么将
x
x
x 赋值为
y
y
y ;否则
−
−
c
n
t
--cnt
−−cnt 。
当
x
=
y
x=y
x=y 时,
+
+
c
n
t
++cnt
++cnt 。
最后输出
x
x
x 即可。
4. 二进制拆分
在方法1中,桶排因为内存爆炸被淘汰了。但是为了利用起桶排的线性复杂度,我们可以使用二进制拆分。
因为
a
i
a_i
ai 是32位无符号整数,我们把它按二进制拆分成32个数位。对于非0位,让这一位对应的桶+1。
然后把32个桶检查一遍,对于数量超过
n
2
\dfrac n2
2n 的就可以计入答案。
因为我们知道,如果答案的某一位为1,那么超过一半的数这一位都为1;如果答案的这一位为0,那么即使除了众数以外所有数这一位都为1,也不可能超过
n
2
\dfrac n2
2n 。
时间复杂度
Θ
(
32
n
)
\Theta(32n)
Θ(32n) 。看起来常数非常大,但是这个其实是32次位运算,卡一卡是可以过去的。
值得一提的是,如果题目中众数的出现频率不低于0.5,而不是大于0.5,那么这个方法不可行,因为在极限情况下会遇到桶内数值为
n
2
\dfrac n2
2n 的情况,而这就意味着答案的这一位无法被确定。
就写这么多吧
最后补一句,记得加读入优化