C++ 求多数元素的分治算法?

C++ 求多数元素的分治算法?,c++,algorithm,C++,Algorithm,如果一个数组有一半以上的元素是相同的,则称该数组有一个多数元素。是否有分治算法来确定数组是否有多数元素 我通常会执行以下操作,但不是使用分而治之。我不想使用这个算法 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] ==

如果一个数组有一半以上的元素是相同的,则称该数组有一个多数元素。是否有分治算法来确定数组是否有多数元素

我通常会执行以下操作,但不是使用分而治之。我不想使用这个算法

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;
}

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

首先查找中间值,例如使用霍尔的选择算法。如果一个值构成了大多数元素,中位数必须有那个值,所以我们刚刚找到了我们要寻找的值

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

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

然后继续搜索介于第25百分位和中位数之间的项目,以及介于第75百分位和末尾之间的项目

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


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

有,它不要求元素具有顺序

从形式上讲,我们处理的是所谓的包。下面,对于多集S,让:

ve,S是元素e在S中的重数,即如果e根本不是S的成员,则重数出现的次数为零。 S是S的基数,即S计数重数中的元素数。 ⊕ 是多集和:如果S=L⊕ 然后S包含L和R计数重数的所有元素,即ve;S=ve;L+ve;R表示任何元素e。这也表明多重性可以通过“分而治之”来计算。 [x] 是小于或等于x的最大整数。 S的多数元素m(如果存在)是2 vm的元素;S>S

让我们称L和R为s的分裂,如果⊕ R=S和偶数分裂if | L-R |≤ 1.也就是说,如果n=S是偶数,L和R正好有S元素的一半,如果n是奇数,则一个有基数[n/2],另一个有基数[n/2]+1

对于S任意拆分为L和R,有两个观察结果:

如果L和R都没有多数元素,那么S不能:对于任何元素e,2ve;S=2ve;L+2ve;R≤ L+R=S

如果L和R中的一个有重数为k的多数元素m,那么只有当它的另一半有重数R,且2k+R>S时,它才是S的多数元素

下面的算法Mactorys返回一对m,k,表示m是出现k次的多数元素,或者不返回:

如果S为空,则返回none;如果S只有一个元素m,则返回m,1。否则: 将S均匀地分成两半L和R。 设m,k=majorityL,如果不是无:

a。设k'=k+vm;R

b。如果2 k'>n,则返回m,k'

否则,设m,k=majorityR,如果不是无:

a。设k'=k+vm;L

b。如果2 k'>n,则返回m,k'

否则返回none。 请注意,即使拆分不是偶数拆分,该算法仍然正确。但在实践中,平均分割可能会表现得更好

附录

在上述算法描述中明确了终端案例。一些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;
}

一种更简单的分治算法适用于存在超过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,但必须小心处理边界情况。

假设数组为1,2,1,1,3,1,4,1,6,1

如果一个数组包含超过一半的相同元素,那么应该有一个位置,其中两个连续元素是相同的


在上述示例中,观察1重复了一半以上。索引从0开始索引2和索引3具有相同的元素

他提到了另一个B-M算法:为什么你不想使用Boyer-Moore,它如此高效?我实际上想使用分治算法,因为这个原因我不想使用Boyer-Moore算法。我不认为这是一个好主意:如果你将阵列分成两半,最频繁的元素可能不是两半中最频繁的元素。例如,cccaaaabbbbccc。@YvesDaoust:该示例没有多数元素。多数元素在16次中至少出现9次。@mAlters:相同的限制适用于多数元素:它不会在所有细分中都是多数元素,并且D&Q循环不会直接出现
[x]是楼层功能?[x]是楼层功能;m的计数函数也可以通过分治来实现:计数m,L⊕ R=计数m,L+计数m,R,其中⊕ 表示多集和。我将编辑答案以使其更清晰。大小:4,数组:1 2 3 1,当它应该返回null时仍然返回1