已知一个整数序列A = (a0,a1,...,an-1),其中0<=ai<n(0<=i<n).若存在ap1 = ap2 = ...=apm = x且m>n/2(0<=pk<n,1<=k<=m),则称x为A的主元素。(就是数组中存在大于n/2个元素都等于x).例如:A = (0,5,5,3,5,7,5,5),则5为主元素;又如A = (0,5,5,3,5,1,5,7),则A中没有主元素。假设A中的n个元素保存在一个一维数组中,请设计出一个尽可能高效的算法,找出A的主元素。若存在主元素,则输出该元素;否则输出-1.要求:
(1)给出算法的基本设计思想。
(2)根据设计思想,采用C或C++或java语言描述算法,关键之处给出注释
(3)说明你所设计的算法的时间复杂度和空间复杂度
(1)算法的主要设计思想:算法的策略是从前往后扫描数组元素,标记出一个可能成为主元素的元素Num。然后重新计数,确认Num是否是主元素。
算法分为两步:
1.选取候选的主元素。依次扫描所给数组中的每一个整数,将第一个遇到的整数Num保存到c中,记录Num出现的次数为1;若遇到的下一个整数仍等于Num,则计数加1,否则计数减1;当计数减到0时,将遇到的下一个整数保存到c中,计数重新记为1,开始新一轮计数,即从当前位置开始重复上述过程,直到扫描完全部数组元素(因为主元素的个数肯定大于数组元素个数的一半,若是Num加1,若不是Num则减1,若是主元素,那么Num肯定不会是0,就算主元在前面一部分,然后Num减到0,换成其他元素,因为其他元素个数肯定要小于数组元素一半,那么其迟早减到0,后面部分肯定是主元素,那么主元素的Num肯定不为0)
2.判断c中元素是否是正真的主元素。再次扫描该数组,统计c中元素出现的次数,若大于n/2,则为主元素;否则,序列不存在主元素。例如该例子:A = (0,5,5,3,5,1,5,7),Num = 1,也是大于0,所以需要验证
int Majority(int *A,int n)
{
int i,c,count = 1;//c是用来存放记录数组A中的某一元素,若是主元素就输出c,这样就不需要再找
c = A[0];//首先把数组A的第一个元素赋给c,设置A[0]为主元素,一步步向后扫描
for(i = 1; i < n; ++i)//查找候选主元素
{
if(c == A[i])
count++;//对A中的候选主元素计数,若是相等则加1
else
{
if(count > 0)//规定要count为0的时候换元素,若count大于0肯定要继续扫描
count--;
else
{
c = A[i];//若是count=0,那么就将此时不和候选主元素相同的元素作为候选主元素
count = 1;//因为此时有一个元素,故count = 1
}
}
}
if(count > 0)//来验证候选主元素是否为真正的主元素
{
for(i=count=0; i<n; ++i)//首先规定count和i为0,从头开始统计数组中和候选主元素相等元素的个数
{
if(A[i] == c)
count++;
}
}
if(count>n/2)
return c;//如果大于n/2,那么就是主元素
else
return -1;//不存在主元素
}
(3)时间复杂度为O(n)(总共扫描元素为2n次,所以时间复杂度为O(n)),没有使用除该空间的其他额外空间,所以空间复杂度为O(1)
完整代码
//假设整数序列为10个数
#include<stdio.h>
//函数说明
int Majority(int *A,int n);
int main(void)
{
int A[10] = {1,3,0,1,1,8,1,1,1,9};
int x = Majority(A,10);
if(-1 == x)
printf("没有主元素\n");
else
printf("主元素为:%d\n",x);
return 0;
}
int Majority(int *A,int n)
{
int i,c,count = 1;
c = A[0];
for(i = 1; i < n; ++i)
{
if(c == A[i])
count++;
else
{
if(count > 0)
count--;
else
{
c = A[i];
count = 1;
}
}
}
if(count > 0)
{
for(i=count=0; i<n; ++i)
{
if(A[i] == c)
count++;
}
}
if(count>n/2)
return c;
else
return -1;
}