分治法求众数问题

问题描述:给定含有n个元素的多重集合S,每个元素在S中出现的次数称为该元素的重数。多重集S中重数最大的元素称为众数。要求对给定的又n个自然数的组成的多重集S,计算S的众数及其重数。

读完问题我们发现起始求众数就是求一个数组里面出现最多的数及其出现的次数,那么我们与其从左往右依次寻找还不如直接从中间往左右寻找更轻松,假设我们先求出中位数的重数,如果发现左边的数的个数小于这个重数那么我们就不用再往左边寻找了,因为左边已经没有比这个中位数的重数更大的数了,右边同理。

经过上面的分析我们再把问题细化一下,如果我找出这个中位数在这个数组中第一次出现的位置,取出从这个位置再加这个中位数的重数个连续的数组成一个子数组,那么如果这个子数组左边的数目小于这个子数组的个数,也就是重数个数,那么左边不会有众数出现,右边同理。

接下来就是代码了:

int n = 0 ; //存储众数
int s = 0 ; //存储众数的重数

int count(int a[], int p, int q){
/*统计中位数的重数*/
    int m = a[(p+q)/2] ;
    int counts = 0 ;
    for(int i=p; i<q; i++){
        if(a[i]==m)
            counts++ ;
    }
    return counts ;
}

int start(int a[], int p, int q){    
/*求中位数第一次出现的位置*/
    int x = 0 ;
    for(int i=p; i<q; i++){
        if(a[i]==a[(p+q)/2])
            x = i ;
            break ;
    }
    return x;
}

void find(int a[], int p, int q){
    int m = (p+q)/2 ;
    int ms = count(a, p, q) ;
    int left = start(a, p, q) ;
    if(ms > s){        //如果当前中位数的重数大于众数的重数,则代替众数
        s = ms ;       
        n = a[m] ;
    }
    if(q-(left+ms) > s)        //如果右边“空余”位置大于重数,往右边递归
        find(a, left+ms, q) ;  
    if(left>s)                 //如果左边“空余”位置大于重数,望左边递归
        find(a, 0, left) ;
}

没看懂的话欢迎骚扰    -_-

猜你喜欢

转载自blog.csdn.net/dms2017/article/details/88806687