有一种算法叫大多数算法,大多数意思是给定一个数组,已知里面有一个数的统计个数超过了总数的一半。比如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;
}