数组中的大多数元素分治 O(N.log(N))

Posted

技术标签:

【中文标题】数组中的大多数元素分治 O(N.log(N))【英文标题】:Most Element in Array Divide-And-Conquer O(N.log(N)) 【发布时间】:2018-07-12 23:24:41 【问题描述】:

一个数组a[],有N个元素,允许重复,如果超过一半的内容等于v,则称其“主要包含av元素”。给定数组a[],它旨在绘制一个有效的算法(在时间 N.log (N) 并使用分治法)检查它是否包含多数元素并确定它。考虑数组元素之间唯一可用的比较操作,即相等(a [i] == a [j]),在恒定时间内执行。 (提示:在算法中,将数组to[]分成两个子数组a1[]和a2[],每个子数组都是a[]大小的一半。如果a[]中的大部分元素是v,那么v一定是也是 a1 [] 或 a2 [] 或两者的大多数元素)。

int main() 

    int a[12] = 5, 9, 3, 13, 5, 21, 5, 7, 17, 12, 5, 6;
    int N = 12, lo = 0, hi = N - 1, mid,i,j;

    mid = lo + (hi - lo) / 2;
    int n1 = mid - lo + 1;
    int n2 =  hi - mid;
    int a1[n1],a2[n2];

    /* Copy data to temp arrays a1[] and a2[] */
    for (i = 0; i < n1; i++)
        a1[i] = a[lo + i];
    for (j = 0; j < n2; j++)
        a2[j] = a[mid+1+j];


    while (i < n1 && j < n2) 

        if(a1[i]==a2[j])

        else if()


        else


        

    
    return 0;

我在继续使用相等操作比较辅助数组以查看大多数元素是否在 a1[] 或 a2[] 或两者上的方式上遇到了麻烦!

【问题讨论】:

@AlbinPaul 似乎不允许 OP 排序。他不能使用其他比较而不是平等。 "如果 a [] 的大部分元素是 v,则 v 必须也是 a1 [] 或 a2 [] 或两者的大多数元素" - 但是逆推论不正确: 即使 v 在 eg 中占多数a1[] 不一定是a[] 中的多数。 D-and-C 是必需的吗?有一个线性算法。 【参考方案1】:

这是一个符合描述的 Python 实现(抱歉,我不精通 C,但我认为它是非常简单的代码)。我们可以按照记录的每个部分的返回值和索引进行检查,以了解其工作原理。

# Returns v if v is a majority;
# otherwise, returns None
def f(arr, low, high):
  if low == high:
    return arr[low]

  if low + 1 == high:
    return arr[low] if arr[low] == arr[high] else None

  n = high - low + 1
  mid = (low + high) / 2

  l = f(arr, low, mid)
  r = f(arr, mid + 1, high)

  print 'n: ' + str(n) + '; l: ' + str(l) + '; r: ' + str(r) + '; L: ' + str((low, mid)) + '; R: ' + str((mid + 1, high))

  if l == r:
    return l

  counts = [0, 0]

  for i in xrange(low, high + 1):
    if arr[i] == l:
      counts[0] = counts[0] + 1
    if arr[i] == r:
      counts[1] = counts[1] + 1

  if l and counts[0] * 2 > n:
    return l

  if r and counts[1] * 2 > n:
    return r

  return None

输出:

a = [5, 9, 3, 5, 5, 21, 5, 7, 17, 5, 5, 5]

print f(a, 0, len(a) - 1)

"""
n: 3; l: None; r: 3; L: (0, 1); R: (2, 2)
n: 3; l: 5; r: 21; L: (3, 4); R: (5, 5)
n: 6; l: None; r: 5; L: (0, 2); R: (3, 5)
n: 3; l: None; r: 17; L: (6, 7); R: (8, 8)
n: 3; l: 5; r: 5; L: (9, 10); R: (11, 11)
n: 6; l: None; r: 5; L: (6, 8); R: (9, 11)
n: 12; l: None; r: 5; L: (0, 5); R: (6, 11)
5
"""

【讨论】:

【参考方案2】:

我认为函数应该:

1) 对数组的前半部分递归调用自身(返回答案a)

2) 为数组的后半部分递归调用自身(返回答案 b)

3) 循环遍历数组并计算有多少匹配 a/b 并返回匹配最多的那个

请注意,在任何阶段都不需要实际复制数组,因为它永远不会被修改,只需传入一个索引作为子数组的开始和长度。

【讨论】:

我认为您的描述将返回 1 用于输入 1, 1, 1, 2, 2, 3。但 1 不是多数。 在你解决这个问题之前我不赞成:)【参考方案3】:

这可能不是您正在寻找的答案。但是有一个有趣的概率方法来解决这个问题。 可以选择数组的某个位置x,统计array[x]出现的次数,判断是否出现>=array.size()/2。

如果有一个元素填充了数组的一半以上,那么每次迭代随机选择它的位置的机会是 > 1/2。

因此,如果您进行 30 次迭代,则选择“主导”元素的机会为 (1 - (1/2)^30),这对于几乎所有应用程序都是可以的。

复杂度为 O(numberOfIterations * arraySize)

这里是代码 (:.

它是在 C++ 上的,但我敢打赌,你可以毫不费力地将它翻译成 C。

#include <vector>
#include <iostream>


int arraySize, numberOfIterations;

int count(int element, std::vector<int>& array)

    int count = 0;
    for(const int& number : array)
    
        count += (number == element);
    
    return count;



int main()

    srand(time(0));

    std::cin >> arraySize;
    std::vector<int> arr(arraySize);

    for(int i = 0; i < arraySize; ++i)
    
        std::cin >> arr[i];
    

    std::cin >> numberOfIterations;

    for(int i = 0; i < numberOfIterations; ++i)
    
        int idx = rand() % arraySize;
        int freq = count(arr[idx], arr);
        //std::cout << idx << std::endl;
        if(freq > arraySize / 2)
        
            std::cout << "The element = " << arr[idx] << " dominates the array provided " << std::endl;
            return 0;
        
    
    return 0;

【讨论】:

以上是关于数组中的大多数元素分治 O(N.log(N))的主要内容,如果未能解决你的问题,请参考以下文章

数组中的逆序对与归并中的分治思想

存在重复元素

设计一个采用 O(n log n) 确定的分治算法

复杂度为 O(n log n) 的分治算法

最长上升子序列(LIS)的n*log(n)求法

c_cpp 使用后缀数组查找最长公共前缀(LCP)。复杂性:SA = O(n.log(n)),LCP = O(n)