题目描述:
已知一个整数序列A={a0,a1,a2,...an-1} 其中0<=ai<n(0<=i<n).若存在
ap1=ap2=...=apm=x且m>n/2(0<=pk<n,1<=k<=m),则称x为A的主元素。如
A={0,5,5,3,5,7,5,5},则5为主元素,又如A={0,5,5,3,5,1,5,7},则A中没有
主元素,若有主元素输出该元素,否则输出-1.
分析:因为有这个条件0<=ai<n(0<=i<n),我们可以自然而然的想到开辟一个n个长度的数组,用下标来代替0,1,2...n-1的值,数组里面自然就是存储其出现的次数咯,然后判断其个数即可,代码如下:
#include<stdio.h>
#define N 8
int A[N];
int main() {
for(int i=0;i<N;i++){
int k;
scanf("%d",&k);
A[k]++;
}
int ok=1;
for(int i=0;i<N;i++){
if(A[i]>N/2) {
ok = 0;
printf("%d\n",i);
break;
}
}
if(ok)
printf("-1\n");
return 0;
}
当然这种思路的时间复杂度为O(n),空间复杂度为O(n),能有更好的办法吗,将空间复杂度将为O(1)呢?在王道上看到这样一种思想,就是用一个count来记录num,如果下次出现的还是num,则count加一,否则减一,若为主元素,则在抵消的过程中它的count最终是大于0,而不是主元素的话在抵消的过程中会变为0。具体算法描述:
- 选取候选的主元素:依次扫描所给数组中的每个整数,将第一个遇到的整数num保存到c中,而用计算器count来保存出现的次数,下次再出现num则计数器加1,否则计数器减1;当计数器减到0,遇到下一个整数保存在c中,计数器重新记数为1,开始新一轮计数,即从当前位置开始重复上述过程,直到扫描完全部的数组元素。
- 判断c中是否是正真的主元素:再次扫描数组,统计c在数组中出现的次数是否大于n/2,大于则为中元素否则序列不存在主元素。
void Majority(int A[],int n){
int i,c,count=1;
c = A[0];
for(i=1;i<n;i++){
if(A[i] == c){
count++;
}else{
if(count>0) count--;
else {
c = A[i];
count = 1;
}
}
}
if(count>0){
count = 0;
for(i=0;i<n;i++){
if(A[i]==c) count++;
}
}
if(count>n/2) printf("%d\n",c);
else printf("-1\n");
}
实现程序的时间复杂度为O(n),空间复杂度为O(1).