首先感谢mxw老铁,提出了这个问题。
问题描述
【问题描述】
已知一个整数序列A长度为N其中若存在a且a的个数大于N/2则称为A的主元素
例如0 5 5 3 5 7 5 5 则为主元素 5
又如0 5 5 3 5 1 5 7则中没有主元素。
假设中的个元素保存在一个一维数组中,请设计一个尽可能高效的算法,找出的主元素。若存在主元素则输出该元素否则输出
【输入形式】
一个整数数组
【输出形式】
主元素
【样例输入】
0 5 5 3 5 7 5 5
【样例输出】
5
一、思考临界时的情况
临界时,即某一元素恰好为主元素
我们拿十个数字为例。
比如
1 2 3 2 4 2 5 2 6 2
显然这是十个数字,只要在任意一个地方插入2这字数字 那么2就成了这个数组的主元素
二、想方法
(1)笨笨的方法:
暴力二重循环
int time
for(int i=0;i<n;i++)
{
time=0;
for(int j=0;j<n;j++)
{
if(a[i]==a[j])
{
time++;
}
}
if(time>n/2)
printf("yes");
}
显然不可能用这种聪(ben)明(dan)方法,时间复杂度为O(n^2);
(2)稍微聪明点的方法
假设主元素为m,那么想,数组中与m相同的必然比与m不同的多
也就是
相同-不相同>0
于是我们想到设置一个计数器count(默认值为1),去记录与m相同的次数。
相同加一,不相同减一,最后观看count是否大于0
如果大于零,那么就是主元素。
但似乎和笨蛋方法一样,也需要二重循环,时间复杂度还是O(n^2)
我们需要做出优化。
(3)聪明的方法
二重循环就是老慢牛,谁用谁拉稀。
为此,我们想,能否用一个循环,只遍历一遍数组,就干出来它。
先假设这个数组就是有主元素的
我们想,最极致的例子就是 相同的比不相同的多一个(奇数)
我们依次把数组中从头到尾,任意两个不相同的都干掉,那么最后剩下的那一个一定是主元素。
不太极致一些,相同的比不相同的多两个,那么都干掉以后,就剩下两个一模一样的,那么,该数值的元素就是主元素
以此类推,会有更不极致的例子,最后剩下若干个一模一样的,那么同样的,这个元素就是主元素。
为此我们借用方法二的计数器方法,相同count+=1,不相同count-=1,当为零时说明相同和不相同的抵消了,则前面的全扔掉,从下一个开始重复上叙述过程。
#include<stdio.h>
int main()
{
int a[100];
int i=0,n;
while(1)
{
scanf("%d",&a[i]);
i++;
char c=getchar();
if(c=='\n')break;
}
n=i;
int m,count;
m=a[0];
count=1;
for(i=1;i<n;i++)
{
if(m==a[i])
{
count++;
}
else
{
if(count>0)
{
count--;
}
else
{
m=a[i];
count=1;
}
}
}
printf("%d是主元素",m);
return 0;
}
然后,我们抛弃假设,如果不知道数组是不是存在主元素呢
直接在下面再用m遍历一遍,判断次数是否大于n/2;
这样的时间复杂度依然是O(2N)=O(N),
#include<stdio.h>
int main()
{
int a[100];
int i=0,n;
while(1)
{
scanf("%d",&a[i]);
i++;
char c=getchar();
if(c=='\n')break;
}
n=i;
int m,count,x;
m=a[0];
count=1;
for(i=1;i<n;i++)
{
if(m==a[i])
{
count++;
}
else
{
if(count>0)
{
count--;
}
else
{
m=a[i];
count=1;
}
}
}
x=0;
for(i=0;i<n;i++)
{
if(m==a[i])
{
x++;
}
}
if(x>n/2)
printf("%d",m);
else
printf("-1");
return 0;
}