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

7
如果一个数组中超过一半的元素相同,则称其具有主要元素。是否有一种分治算法可确定数组是否具有主要元素?
我通常会这样做,但它不使用分治法。我不想使用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;
}

为什么你不想使用Boyer-Moore算法,它非常高效? - user1196549
我实际上想使用分治算法来完成它,因此我不希望使用Boyer-Moore算法。 - user4652599
@YvesDaoust:这个例子没有多数元素(多数元素至少会在16个元素中出现9次)。 - MSalters
@MSalters:大多数元素也受到相同的限制:它不会在所有子分区中成为占绝大多数的元素,D&Q递归也并不简单。 - user1196549
@MSalters:在下一个细分级别上,这个不成立。CCCC|CAAA。 - user1196549
显示剩余5条评论
4个回答

4

有一种叫做多重集的概念存在,它不需要元素按顺序排列。

严格来说,我们处理的是多重集(也称为袋子)。在下文中,对于一个多重集S,请定义:

  • v(e,S) 是元素 e 在集合 S 中出现的次数,即它的重复度(如果 e 不是 S 的成员,则重复度为零。)
  • #S 是集合 S 的基数,即考虑重复度的情况下 S 中元素的数量。
  • ⊕ 表示多重集和:如果 S = LR,则 S 包含所有计算重复度后来自 LR 的元素,即对于任何元素 e,都有 v(e;S) = v(e;L) + v(e;R)。(这也表明可以通过“分而治之”计算重复度。)
  • [x] 表示小于或等于 x 的最大整数。
如果存在,则S的大多数元素m是满足2v(m;S)>#S的元素。
如果LR = S,则称LRS的一个分割,并称|#L - #R| ≤ 1的分割为偶数分割。也就是说,如果n=#S是偶数,则LR恰好有S的一半元素,如果n是奇数,则一个集合的基数为[n/2],另一个集合的基数为[n/2]+1。
对于任意S的分割为LR,有两个观察结果:
  1. 如果既没有L也没有R有多数元素,则S也没有:对于任何元素e,2 ve; S)= 2 ve; L)+ 2 ve; R)≤#L+#R=#S

  2. 如果LR中的一个具有多数元素m,其重复次数为k,则只有当另一半中的重复次数为r时,它才是S的多数元素,其中2(k+r)> #S

下面的算法majorityS)返回一个二元组(mk),表示m是具有k个出现次数的多数元素,或者返回none

  1. 如果 S 为空,则返回 none;如果 S 只有一个元素 m,则返回 (m,1)。否则:
  2. S 平均分成两半 LR
  3. 如果 L 不是 none,则令 (m,k) = majority(L):

    a. 令 k' = k + v(m;R)。

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

  4. 否则,如果 R 不是 none,则令 (m,k) = majority(R):

    a. 令 k' = k + v(m;L)。

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

  5. 否则返回 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;
}

[x] 是向下取整函数吗? - user1084944
[x] 是向下取整函数;而 m 函数的计数也可以通过分治法来执行:count(m, LR) = count(m, L) + count(m, R),其中 ⊕ 表示多重集合和。(我会编辑答案以使其更清晰。) - halfflat

4
我至少可以看到一种分治算法。
首先,通过Hoare's Select算法找到中位数。如果一个值形成了大多数元素,那么中位数必须具有该值,因此我们刚刚找到了我们要查找的值。
然后,找到(例如)第25和75百分位项目。同样,如果有一个多数元素,则至少其中一个需要具有与中位数相同的值。
假设您还没有排除存在多数元素的可能性,您可以继续搜索。例如,假设第75个百分位等于中位数,但第25个百分位不等于中位数。
然后继续搜索介于第25个百分位和中位数之间以及介于第75个百分位和末尾之间的项目。
继续找到每个包含与中位数相同值的元素末尾的分区的中位数,直到确认或否认存在多数元素为止。
顺便说一句:我不太清楚Boyer-Moore如何用于此任务。Boyer-Moore是在字符串中查找子字符串的一种方式。

他提到了另一个B-M算法:http://www.cs.utexas.edu/~moore/best-ideas/mjrty/ - MBo

2

如果存在超过1/2的元素相同,并且有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
大小:4,数组:1 2 3 1,当它应该返回 null 时仍然返回 1。 - Sparker0i

0
假设该数组为1、2、1、1、3、1、4、1、6、1。
如果一个数组包含超过一半的相同元素,那么应该有一个位置,在该位置上两个连续的元素是相同的。
在上面的例子中,观察到1重复了超过一半的次数。而索引(索引从0开始)索引2和索引3具有相同的元素。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接