大多数算法及其推广

有一种算法叫大多数算法,大多数意思是给定一个数组,已知里面有一个数的统计个数超过了总数的一半。比如1,2,3,1,1,1 ,那么1就是大多数。有一个简单的算法就是排序,然后取中位数。时间复杂度为O(nlogn)。那么有没有更快速的算法呢,这里介绍一种O(n)的算法。其原理就是开一个临时变量,和引用计数,然后遍历数组,如果已经存在于临时变量计数就加1,否则减1,当减到0的时候重新保存变量,计数置为1。最后如果引用计数大于0那么这个临时变量就是大多数。算法很简单也很巧妙,实现代码如下:

// 这是大多数算法的实现,先决条件是容器里面有一个元素
// 超过容器个数的一半,否则返回值不准。
template <class InputIterator>
typename iterator_traits<InputIterator>::value_type
Major( InputIterator first, InputIterator end )
{
    if ( first == end )
    {
        return 0;
    }
    
    typename iterator_traits<InputIterator>::difference_type n = 1;
    InputIterator itor = first++;
    for ( ; first != end; first++ )
    {
        *first == *itor ? n++ : n--;
        if ( n == 0 )
        {
            itor = first;
            n++;
        }
    }
    return *itor;
}

那么,引申出来,假设一个数组里面有一个数超过了总数的1/3,请找出这个数。这个算法跟上面的大多数算法其实是一类的。

虽说是一类,但显然不能直接套用上面的实现。在陈利人的微博里给出了俄罗斯方块消除法的思路,采用消除法,如果三个数都不相同,我们就不计数,就抛弃他们,直到最后不足以组成3个不同数。剩下的最多数就是结果。比如,1,2,3,1,2,3,1,将1,2,3一组抛弃,最后剩下1,则为结果。为什么说这种方法可行呢?我们可以先排序,这种排序是按照个数多少从前往后,上面的数组排序之后就是1,1,1,2,2,3,3。显然如果要完全消除1至少要6个其他数,而大于1/3保证了其他剩余数没有这么多。

具体分析:假设t为大多数,个数为n,n为总数,m/n>1/3。这样从头到尾遍历数组组成不相同的一个组合就有两种情况,

1. t不在组合里,m/(n-3)就是消除之后的比例,显然m/(n-3)>m/n。

2. t在组合里,则(m-1)/(n-3)>m/n。

所以始终能保证t在剩余的数组里占据超过1/3的量。

其实上面两个问题是此类问题的两个特殊化,即对于一个数组n,存在t,在n中的个数占有超过1/k,其中k>=2,求t。

例子1中,也是消除法,当两个数不一致时候就消除。最后剩下的就是t。


代码如下:


// 这是大多数算法的实现,先决条件是容器里面有一个元素
// 超过容器个数的三分之一,否则返回值不准。
template <class InputIterator>
typename iterator_traits<InputIterator>::value_type
Major_third( InputIterator first, InputIterator end )
{
    if ( first == end )
    {
        return 0;
    }

    typedef typename iterator_traits<InputIterator>
        ::difference_type diff_type;
    diff_type n1, n2, n3;
    n1 = n2 = n3 = 0;
    InputIterator Itor1, Itor2, Itor3;
    Itor1 = Itor2 = Itor3 = end;

    for ( ; first != end; ++first )
    {
        bool bExist = true;
        if ( n1 && *first == *Itor1 ) n1++;
        else if ( n2 && *first == *Itor2 ) n2++;
        else if ( n3 && *first == *Itor3 ) n3++;
        else bExist = false;

        if ( n1 && n2 && n3 )
        {
            if ( --n1 == 0 ) Itor1 = end;
            if ( --n2 == 0 ) Itor2 = end;
            if ( --n3 == 0 ) Itor3 = end;
        }
        else
        {
            if ( 0 == n1 )
            {
                Itor1 = first;
                ++n1;
            }
            else if ( 0 == n2 )
            {
                Itor2 = first;
                ++n2;
            }
            else if ( 0 == n3 )
            {
                Itor3 = first;
                ++n3;
            }
        }
    }

    if ( n1 >= n2 && n1 >= n3 ) return *Itor1;
    if ( n2 >= n1 && n2 >= n3 ) return *Itor2;
    if ( n3 >= n1 && n3 >= n2 ) return *Itor3;
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值