用于寻找多数元素的分治算法?

时间:2015-03-10 06:20:00

标签: c++ algorithm

如果超过一半的元素是相同的,则称一个数组具有多数元素。是否存在用于确定数组是否具有多数元素的分而治之算法?

我通常会做以下事情,但它并没有使用分而治之。我不想使用Boyer-Moore算法。

int find(int[] arr, int size) {
    int count = 0, i, mElement;

    for (i = 0; i < size; i++) {
        if (count == 0) mElement = arr[i];

        if (arr[i] == mElement) count++;
        else count--;
    }

    count = 0;
    for (i = 0; i < size; i++) {
        if (arr[i] == mElement) count++;
    }

    if (count > size / 2) return mElement;
    return -1;
}

4 个答案:

答案 0 :(得分:4)

我至少可以看到一种分而治之的方法。

首先查找中位数,例如使用Hoare的Select算法。如果一个值构成了元素的大多数,则中位数必须具有该值,因此我们只是找到了我们正在寻找的值。

从那里,找到(例如)第25和第75百分位数的项目。同样,如果有多数元素,那么至少有一个元素需要与中位数具有相同的值。

假设您还没有排除多数元素,您可以继续搜索。例如,让我们假设第75百分位数等于中位数,但第25百分位数不是。

然后继续搜索第25百分位数和中位数之间的项目,以及第75百分位数和结束点之间的中间位置。

继续查找每个分区的中位数,该分区必须包含与中位数相同的元素的结尾,直到您确认或否认存在多数元素为止。

顺便说一句:我不太清楚Boyer-Moore将如何用于此任务。 Boyer-Moore是一种在字符串中查找子字符串的方法。

答案 1 :(得分:3)

有,并且不需要元素有订单。

为了正式,我们正在处理multisets(也称为 bags 。)在下文中,对于多集 S ,请:

  • v e S )是 S <中 e 元素的多重性/ em>,即它出现的次数(如果 e 根本不是 S 的成员,则多重性为零。)
  • #S S 的基数,即 S 计算多重性的元素数。
  • ⊕是multiset sum:如果 S = L R 那么 S 包含所有元素 L R 计算多重性,即 v e ; S )= < em> v e ; L )+ v e ; R < / em>)任何元素 e 。 (这也表明多重性可以通过“分而治之”来计算。)
  • [ x ]是小于或等于 x 的最大整数。

S 的多数元素 m (如果存在)是2 v m 的元素>; S )&gt; #S

如果 L ,让我们调用 L R S 拆分 R = S 甚至拆分如果| #L - #R | ≤1。即,如果 n = #S 是偶数, L R 只有一半的元素 S ,如果 n 是奇数,则其中一个具有基数[ n / 2]而另一个具有基数[ n < / EM> / 2] +1。

对于 S L R 的任意分割,有两个观察结果:

  1. 如果 L R 都没有多数元素,则 S 不能:对于任何元素 e ,2 v e ; S )= 2 v e ; L )+ 2 v e ; R )≤ #L + #R = #S

  2. 如果其中一个 L R 的多数元素 m 具有多重性 k ,那么它只是 S 的主要元素,只要它在另一半中具有多重性 r ,其中2( k + r )&gt; #S

  3. 以下算法多数 S )会返回一对( m k ),表示 m k 出现的主要元素,或 none

    1. 如果 S 为空,请返回 none ;如果 S 只有一个元素 m ,则返回( m ,1)。否则:
    2. S 分成两半 L R
    3. 设( m k )= 多数 L ),如果不是

      一个。设 k' = k + v m ; R )。

      湾如果2 k'&gt;则返回( m k'名词

    4. 否则让( m k )= 多数 R ),如果不是< EM>无

      一个。设 k' = k + v m ; L )。

      湾如果2 k'&gt;则返回( m k'名词

    5. 否则返回 none
    6. 请注意,即使拆分不是偶数,算法仍然正确。实际上,均匀分裂可能会表现得更好。

      <强>附录

      在上面的算法描述中明确了终端案例。一些示例C ++代码:

      struct majority_t {
          int m; // majority element
          size_t k; // multiplicity of m; zero => no majority element
      
          constexpr majority_t(): m(0), k(0) {}
          constexpr majority_t(int m_,size_t k_): m(m_), k(k_) {}
      
          explicit operator bool() const { return k>0; }
      };
      
      static constexpr majority_t no_majority;
      
      size_t multiplicity(int x,const int *arr,size_t n) {
          if (n==0) return 0;
          else if (n==1) return arr[0]==x?1:0;
      
          size_t r=n/2;
          return multiplicity(x,arr,r)+multiplicity(x,arr+r,n-r);
      }
      
      majority_t majority(const int *arr,size_t n) {
          if (n==0) return no_majority;
          else if (n==1) return majority_t(arr[0],1);
      
          size_t r=n/2;
          majority_t left=majority(arr,r);
          if (left) {
              left.k+=multiplicity(left.m,arr+r,n-r);
              if (left.k>r) return left;
          }
      
          majority_t right=majority(arr+r,n-r);
          if (right) {
              right.k+=multiplicity(right.m,arr,r);
              if (right.k>r) return right;
          }
      
          return no_majority;
      }
      

答案 2 :(得分:0)

更简单的分而治之算法适用于存在多于1/2个元素的情况,对于某个整数k,存在n = 2 ^ k个元素。

FindMost(A, startIndex, endIndex)
{  // input array A

if (startIndex == endIndex)  // base case
        return A[startIndex]; 
x = FindMost(A, startIndex, (startIndex + endIndex - 1)/2);
y = FindMost(A, (startIndex + endIndex - 1)/2 + 1, endIndex);

if (x == null && y == null) 
    return null;
else if (x == null && y != null) 
    return y;
else if (x != null && y == null) 
    return x;
else if (x != y) 
    return null;
else return x

}

可以修改此算法,使其适用于非指数为2的n,但必须小心处理边界情况。

答案 3 :(得分:0)

让我们说阵列是1,2,1,1,3,1,4,1,6,1。

如果一个数组包含多于一半的元素,那么应该有一个两个连续元素相同的位置。

在上面的例子中,观察1重复超过一半。并且索引(索引从0开始)索引2和索引3具有相同的元素。