习题2-19 分治法查找主元(出现次数超过N/2的元素) 数据结构与算法分析in c

问题

找出数组中的主元

设计并实现使用分治求主元的算法

主元(majority element):大小为N数组中出现次数超过N/2的元素

候选元(candidate ele):参见书

注意:不确定数组中是否存在主元!

算法设计

如何优雅的解决这个问题困扰了我一段时间:

简单的方法,自然是遍历一遍数组,时间复杂度O(N)

int find(int a_copy[],int len){

int cnt=0;

int now;

for(size_t i=0;i<len;i++){

if(cnt==0)

now=a_copy[i];

if(now==a_copy[i])

cnt++;

else

cnt--;

}

  

if(Trivialjudge(now)==true)

return now;

else return _INI_MAX_;//(0x7fffffff)

}

直观上理解为,不同的元素相互抵消,最多的元素将留下

但是如果按书上两个两个元素筛选候选元的思想,就比较麻烦,也可以理解为上面那个方法,因为不同的两个元素被抵消了,剩下相同的元素(不能边抵消边融合元素,因为融合后与融合前权重不一样)

  

首先确定此做法的原理:

如果数组中有主元

在数组中,主元的权重>N/2;其他元素的权重最大也小于N/2;

在"抵消"之后,主元被抵消,则其他元素的最大权重-1,主元权重-1;

在"合并"之后,主元权重不变,其他也不变;

所以在"抵消" 后,权重最大的仍然是主元;

  

  

遇到奇数长度的数组怎么办呢?如下图所示,2表示已经合并了,1表示是奇数数组的最后一个元素

|这种方法是我无意中想到的,用一个变量记录末尾的未合并元素数量

|

如果数组中没有主元

不论最后找出来的是什么,只要O(N)时间复杂度的检验,就可以判断找出来的是否是主元

过程分析

先将能合并的合并(同样权重),不能合并的留在后面,每次合并都拷贝在数组最后面;

合并完后,选择第一个元素即是可能的候选元;如果第一个元素都没有,就视为没有候选元

代码

这只是表达思想,实际运用中不用分治;

int findMaj(int a[],int len){

int copy[len+1]; //防止越界

memcpy(copy,a,len*sizeof(int));

if(findCan(copy,len)){

int can=*copy;

for(int i=0,cnt=0;i<len;i++){

if(a[i]==can)

cnt++;

if(cnt>len/2)

return can;

}

}

return 0x7fffffff;

}

int findCan(int a[],int len){

int rea=0;

while(len>=2){

         int len2=0;

    for(int i=0;i<len-1;i+=2)

    if(a[i]==a[i+1])

    a[len2++]=a[i];

                  

    if(len&1){

    rea++;

    memcpy(a+len2,a+len-1,sizeof(int)*rea);

    }else{

    memcpy(a+len2,a+len,sizeof(int)*rea);

    }

    len=len2;

}

if(len==0 && rea==0) return 0;

else return rea;

}

https://github.com/Haozun/algorithms/blob/master/V0.0/FindMajorityElement_2-19_dataStructureAnalysisInC.c

时间复杂度

查找候选元的算法运行时间设为T(N),有递推式T(N)=T(N/2)+O(N),解得T(N)=O(N),查找主元的算法P(N)=O(N)

  

  

  

  

猜你喜欢

转载自blog.csdn.net/migeater/article/details/79702109
今日推荐