【经典问题】找一个数组中的主元素

问题:在一个规模为N的数组array[N]中,所谓主元素就是出现次数大于N/2的元素,例如 

                                           3.3.4.2.4.4.2.4.4  有一个主元素为4。

          给出一个算法,如果过半元素存在,就找出来,否则给出报告,要求给出O(N)的算法。

 

 

常规想法

 

(1) 穷举:找出元素中每一个数在数据中的数量。时间复杂度O(N^2)

(2) 排序:先对数组快排,然后重头开始遍历一遍计算每个数的数量。时间复杂度O(N*logN+N)

 

经典算法    裁剪数组算法, 时间复杂度为O(2N ) 

     思想: 如果一个数组array[N],其中有两个元素e1和e2。

      (1) e1不等于e2

      假如e1是数组array[N]的主元素,e2不是。那么e1在array[N]中的数量en>N/2。此时去掉array[N]中的e1和e2两个元素(e1!=e2)。那么新数组array[N-2]中e1的数量为en-1,并且en-1>N/2-1=(N-2)/2。即e1还是新数组array[N-2]的主元素。

      假如e1和e2都不是数组array[N]的主元素,那么去掉e1、e2以后。那么新数组的大小将变成N-2。此时很有可能出现一个新数组的主元素X,此主元素X的数量正好等于X=(N-2)/2+1。但是该主元素就不是原数组的主元素了,因为X= (N-2)/2+1=N/2。那么这样的主元素X我们叫做伪主元素。因此需要通过和裁剪掉元素比较,来确定是否是真正的主元素。

      (2) e1等于e2

      这种情况下不能进行裁剪。只能继续找不同的两个数裁减掉

 

C代码  收藏代码
    1. #include<stdio.h>    
    2. #include<malloc.h>  
    3.  // 算法源代码,裁剪当前数组中相邻的不同元素    
    4.  void main(){    
    5.     int pArr[6]={4,4,4,4,6,6};    
    6.     int arrLength=6;  //数组长度  
    7.     int element=pArr[0];    
    8.     int value=1;  //记录剪裁过程中遇到相同元素的个数  
    9.     int delNum=0; //记录裁剪数组的元素个数  
    10.     int *dArr=(int *)malloc(arrLength*sizeof(int)); //记录被剪裁的数组元素  
    11.     int dTop=0; //当前剪裁数组的索引位置  
    12.        
    13.     for(int i=1;i<arrLength;i++){    
    14.        if(value==0){    
    15.            element=pArr[i];    
    16.        }    
    17.        if(pArr[i]==element) //如果当前数组相邻的元素相等    
    18.            value++;    
    19.        else if(value>0){//如果当前数组相邻的元素不等,则需要裁剪得到新的数组    
    20.            dArr[dTop++]=element;  
    21.            dArr[dTop++]=pArr[i];  
    22.            delNum+=2;    
    23.            value--;    
    24.        }    
    25.     }    
    26.     //如果裁剪之后出现了主元素,那么这个主元素有可能是个伪主元素  
    27.     if(value>(arrLength-delNum)/2){  
    28.         //与裁剪掉的数组元素一一比较  
    29.         for(int j=0;j<delNum;j++)  
    30.             if(element==dArr[j]) value++;             
    31.         //确定真正的主元素  
    32.         if(value>arrLength/2)  
    33.             printf("主元素为:%d\n",element);    
    34.     }

 

需注意的是,该数组的元素之间可能不存在顺序——即不能进行”A[i]<A[j]”的判断,但是可以进行是否相等的判断。

 

O(nlogn)方法:

 

复制代码
#include <stdio.h>

int a[10];

void sort(int *a,int x,int y){
int xx,yy,k;
xx = x;
yy = y;
k = a[x];
if(x>=y)
return;
while(xx!=yy){
while(xx<yy&&a[yy]!=k){
yy--;
}
a[xx] = a[yy];
while(xx<yy&&a[xx]==k){
xx++;
}
a[yy] = a[xx];
}
a[xx] = k;
sort(a,x,xx-1);
sort(a,xx+1,y);
}

int main(){
int i,j,count = 0,sign = -1,n;

scanf("%d",&n);

for(i = 0;i<n;i++){
scanf("%d",&a[i]);
}
sort(a,0,n-1);

for(j = 0;j < n;j++){
if(a[j] == a[j+1])
count++;
else
count = 1;
if(count >= n/2)
{
sign = a[j];
break;
}
}
if(sign == -1)
printf("None");
else
printf("%d",sign);

return 0;
}
复制代码

 

 

O(n)方法:

复制代码
#include <stdio.h>

int getMainElement(int a[],int len){
int i,mainElement,repeatTimes = 0;

for(i = 0;i<len;i++){
if(repeatTimes == 0){
mainElement = a[i];
repeatTimes = 1;
}
else{
if(mainElement == a[i])
repeatTimes++;
else
repeatTimes--;
}
}
return mainElement;
}

int main(){
while(1){
int len,i,mainElement,flagCount = 0;
scanf("%d",&len);
int *a = new int[len];
for(i = 0;i<len;i++){
scanf("%d",&a[i]);
}

mainElement = getMainElement(a,len);

for(i = 0;i<len;i++){
if(mainElement == a[i])
flagCount++;
}

if(flagCount>len/2)
printf("%d\n",mainElement);
else
printf("None\n");
}

return 0;
}
复制代码
posted @ 2013-04-19 23:21  c_cloud  阅读(1225)  评论(0编辑  收藏  举报