Part -1:版权声明:
本文大部分代码来自这篇博文
Part 0:啥是主元素问题
给一个有$n$个元素的数列,保证有一个数$a$出现的次数超过50%,求这个数
Part 1:桶计数做法
桶计数做法是出现一个数,就把这个数出现次数+1,很好懂:
1 |
|
很好懂,时间复杂度$O(N + M)$
但是,一旦利用桶的思想,就不可避免的遇到一个问题:空间
比如我给你的数是这样的:
1 12398517923876091 1000 1000000 10000000000 9999999 2345234618 1239 102358 12398517923876091 12398517923876091 12398517923876091 12398517923876091 12398517923876091 12398517923876091
请问你要开多大的空间?12398517923876091个数组
好,有的同学说可以离散化,我再给你一组数据:
1 2 3 4 5 6 … LONG_MAX 1 1 1 …(LONG_MAX/2个1)
从1到LONG_MAX全部给你,你怎么做?你继续离散化啊?你离散啊你离散啊你离散啊~(别问我为什么数据这么毒瘤,我自己出数据就是这么毒瘤)
Part 2: 排序做法
显然,若一个数列存在主元素,那么这个主元素在排序后一定位于$\frac{n}{2}$的位置
那么我们又有想法了:
1 | #include <bits/stdc++.h> |
看起来不错!$O(NlogN)$的复杂度可还行?
于是,一旦有$NlogN$,就一定有人想出$N$。有吗?还真的有——
Part 3:终极做法
这个数列有个特性:由于主元素的出现的次数超过50%,那么在不断的消掉两个不同的元素之后,最后一定剩下主元素
有的同学会问了:如果数列长度为偶数呢?
我说这位同学,请 你 审 题
给一个有$n$个元素的数列,保证有一个数$a$出现的次数超过50%,求这个数
再读一次:
保证有一个数$a$出现的次数超过50%
还不懂?再读一次:
超过50%
再不懂?滚!
输入时判断与上一次保存的输入是否相同,如不同则删除两数,这里用栈来实现。
#include<stdio.h>
int n,q[10001],top=0,a;
int main()
{
scanf("%d",&n);
while(n--)scanf("%d",&a),q[top++]=a,top=(top>1&&(q[top-1]!=q[top-2]))?top-2:top;
printf("%d",q[top-1]);
}