已知一个整数序列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的主元素。例如A=(0,5,5,3,5,7,5,5),则5为主元素;又如A=(0,5,5,3,5,1,5,7),则A中没有主元素。
假设A中的n个元素保存在一个一维数组中,请设计一个尽可能高效的算法,找出A的主元素。若存在主元素,则输出该元素;否则输出-1
【测试样例】{0,5,5,3,5,7,5,5}
【输出结果】5
【测试样例】{0,5,5,3,5,1,5,7}
【输出结果】-1
算法思路一
- 选取候选的主元素:依次扫描数组中的数,将第一个遇到的数tmp保存在c中,记录tmp出现的次数为1;继续扫描,若下一个数仍等于tmp,则计数+1,否则计数-1,当计数减到0时,将遇到的下一个整数保存到c中,计数重新记为1,开始新一轮计数,重复上述过程,直到扫描完全部数组元素。
- 判断c是否为真正的主元素:再次扫描数组,统计c出现的次数,若大于n/2,则为主元素;否则不存在主元素
上述算法的时间复杂度为O(n),采用空间复杂度为O(1)
int findMainElem(int R[],int n)
{
int i,c,count=1;//count为计数工具,记录候选主元素出现的次数
c=R[0];
for(i=1;i<n;i++)
if(R[i]==c)
count++;
else
if(count>0)
count--;
else
{
c=R[i];
count=1;
}
if(count>0)
for(i=count=0;i<n;i++)//统计候选主元素实际出现的次数
if(R[i]==c)
count++;
if(count>n/2)
return c;
else return -1;
}
算法思路二
- 使用快速排序对数组元素进行排序,为减少时间复杂度
- 新建标志位统计元素出现的个数,从第1个元素开始遍历数组,若和前一元素相同则标志数组元素在之前基础上+1,若不同,则置为0;其中使用max暂存最大值
上述算法的时间复杂度为O(nlog2n),采用空间复杂度为O(1)
//附加快速排序代码
int Partition(int R[],int low,int high)
{
int pivot=R[low];
while(low<high)
{
while(low<high&&R[high]>=pivot)
--high;
R[low]=R[high];
while(low<high&&R[low]<=pivot)
++low;
R[high]=R[low];
}
R[low]=pivot;
return low;
}
void QuickSort(int R[],int low,int high)
{
if(low<high)
{
int pivotpos=Partition(R,low,high);
QuickSort(R,low,pivotpos-1);
QuickSort(R,pivotpos+1,high);
}
}
int findMainElem2(int R[],int n)
{
int i,max,tmp;
int c=0;
QuickSort(R,0,n-1);
int a=R[0];
for(i=1;i<n;i++)
{
if(R[i]!=R[i-1])
{
if(max<c)//始终维持max为最大值
{
tmp=R[i-1];//tmp暂存候选主元素的值
max=c-1;
}
c=0;
}
else
{
c++;//统计量增加
max=c;
tmp=R[i];
}
}
if(max+1>n/2)//由于第一次出现时置为0,所以最终比较结果时需要+1
return tmp;
else
return -1;
}