主元素问题是一道非常经典的著名的问题,乍一看很简单,也很容易写出来,但越往后越难。
问题描述:给定一个长度为n的序列,若当中有数出现的次数>50%,输出这个数;否则输出NO。
还是先上数据:
7
3 3 1 1 3 2 3
输出是:
3
有什么好的方法吗?
建议你花时间先想一想
好啦,开始讲解。
Algorithm1:
将数组进行排序,堆排序或者快速排序,然后找出n/2+1那个位置的数。然后再扫一遍,统计这个数出现的次数。若该次数>n/2,那么这个数就是结果,否则输出NO。
原理:如果有数出现的次数>n/2,那么排序后,它一定会出现在n/2+1的位置上(想一想为什么)。
时间复杂度:O(N log N)
空间复杂度:O(N)
代码:
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<algorithm> #include<cmath> using namespace std; int n,a[1005],i,cnt; int main() { scanf("%d",&n); for(i=1;i<=n;i++) scanf("%d",&a[i]); sort(a+1,a+n+1); for(i=1;i<=n;i++) if(a[i]==a[n/2+1]) cnt++; if(cnt>n/2) printf("%d",a[n/2+1]); else printf("NO"); return 0; }
Algorithm2:
用类似桶排序的方法,开一个数组储存每个数出现的次数。然后再扫一遍,找出那个出现次数最多的数,看看是否>n/2。
这种算法比较好理解。
时间复杂度:O(N+M)
空间复杂度:O(M)
代码:
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<algorithm> #include<cmath> using namespace std; int n,a,x[100005],i,m=-9,cnt=-9,ans; int main() { scanf("%d",&n); for(i=1;i<=n;i++) { scanf("%d",&a); x[a]++; if(m<x[a]) m=x[a]; } for(i=1;i<=m;i++) if(x[i]>ans) { cnt=x[i]; ans=i; } if(cnt>n/2) printf("%d",ans); else printf("NO"); return 0; }
Algorithm3:
O(N)的算法来啦!
绝妙的思想:这样的算法有一个性质:如果在原数组中删除两个不同的数,那么在原数组中出现次数超过50%的数,在新数组中的次数也一定会超过50%。
仔细想一想为什么吧!一旦掌握了这种方法,那么此题就简单了。强烈建议读者自己用这种方法完成此题。
时间复杂度:O(N)
空间复杂度:O(N)
代码也给你们吧:
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<algorithm> #include<cmath> using namespace std; int n,a[1005],x,y,i; int main() { scanf("%d",&n); for(i=1;i<=n;i++) { scanf("%d",&a[i]); if(y==0) { x=a[i]; y++; } else { if(x==a[i]) y++; else y--; } } y=0; for(i=1;i<=n;i++) if(a[i]==x) y++; if(y>n/2) printf("%d",x); else printf("NO"); return 0; }
再强调一遍:强烈建议读者完成此题,并想明白所有细节。
~祝大家编程顺利~