【问题标题】:Find dominant mode of an unsorted array查找未排序数组的主导模式
【发布时间】:2013-02-07 05:00:52
【问题描述】:

注意,这是家庭作业。

我需要找到一个数组的模式(正值),然后如果模式大于 sizeof(array)/2(主要值)则返回该值。有些阵列两者都没有。 这很简单,但是有一个约束条件是在确定之前不能对数组进行排序,另外,复杂度必须在 O(nlogn) 的数量级上。

使用第二个约束和master theorem,我们可以确定时间复杂度 'T(n) = A*T(n/B) + n^D' 其中 A=B 和 log_B(A)=D O(nlogn) 为真。因此,A=B=D=2。这也很方便,因为显性值必须在数组的第 1、第 2 或两半中占主导地位。

使用 'T(n) = A*T(n/B) + n^D' 我们知道搜索函数将在每个级别 (A) 调用自身两次,在每个级别将问题集除以 2 (乙)。我一直在弄清楚如何让我的算法考虑到每个级别的 n^2。

为此编写一些代码:

int search(a,b) {
    search(a, a+(b-a)/2);
    search(a+(b-a)/2+1, b);
}

我在这里缺少的“粘合剂”是如何组合这些划分的功能,我认为这将实现 n^2 的复杂性。这里有一些技巧,其中主导必须是第一半或第二半或两者的主导,不太确定这对我现在的复杂性约束有何帮助。

我已经写了一些小数组的例子,并且我已经画出了它的划分方式。我似乎无法朝着正确的方向找到一个始终返回主导值的单一方法。

在级别 0,函数需要调用自身来搜索数组的前半部分和后半部分。这需要递归,并调用自己。然后在每一层,它需要执行 n^2 次操作。因此,在数组 [2,0,2,0,2] 中,它会将其拆分为对 [2,0] 的搜索和对 [2,0,2] 的搜索并执行 25 次操作。在 [2,0] 上的搜索将调用在 [2] 上的搜索和在 [0] 上的搜索并执行 4 次操作。我假设这些需要搜索数组空间本身。我打算使用 C++ 并使用 STL 中的东西来迭代和计算值。我可以创建一个大数组,然后通过索引更新计数。

【问题讨论】:

  • 你的意思是在数组中找到一个大多数时间出现的值。如果出现 sizeof(array)/2 或更多,返回?
  • 我不明白您在搜索什么或为什么要递归。
  • @Gabe,我猜他正在寻找数组的模式.... # 根据上面的评论,最常出现的情况。分治型递归使其更快。
  • @mike_b:您可以迭代数组,增加map<Value,Count>,然后迭代map 以找到最高计数...将是O(nlogn),因为这是插入地图的成本,无论如何迭代次数都比这少。
  • 我认为他不应该使用更多的内存。您的建议(你们俩)基本上是先进行基数排序,然后从那里确定模式。 存在一个限制,即在确定之前不能对数组进行排序

标签: c++ c algorithm recursion master-theorem


【解决方案1】:

如果某个数字出现超过一半,则可以通过 O(n) 时间复杂度和 O(1) 空间复杂度来完成,如下所示:

int num = a[0], occ = 1;
for (int i=1; i<n; i++) {
    if (a[i] == num) occ++;
    else {
        occ--;
        if (occ < 0) {
            num = a[i];
            occ = 1;
        }
    }
}

由于你不确定这个数是否出现,你需要做的就是先应用上面的算法得到一个数,然后第二次迭代整个数组来得到这个数的出现,并检查它是否大于一半。

【讨论】:

  • @thang 为什么你说我在开玩笑?我的代码有什么问题吗?
  • 我们的目标是找到一个复杂度为 O(nlogn) 的解决方案。这需要使用 log n 步进行递归。
  • @songlj 我认为该算法不适用于某些情况,例如 1, 2, 1, 2, 1, 2, 1, 3, 3, 1 。抱歉,我将其添加为答案,因为我还不能发表评论。编辑:如果我们知道该模式在数组中至少出现 N/2 次,这就是 Boyer-Moore 多数算法。这个stackoverflow.com/questions/3740371/… 讨论了这个特定的模式发现实例。
【解决方案2】:

如果你只想找到一个数组的主要模式,并递归地做,下面是伪代码:

def DominantMode(array):
    # if there is only one element, that's the dominant mode
    if len(array) == 1: return array[0]
    # otherwise, find the dominant mode of the left and right halves
    left = DominantMode(array[0:len(array)/2])
    right = DominantMode(array[len(array)/2:len(array)])
    # if both sides have the same dominant mode, the whole array has that mode
    if left == right: return left
    # otherwise, we have to scan the whole array to determine which one wins
    leftCount = sum(element == left for element in array)
    rightCount = sum(element == right for element in array)
    if leftCount > len(array) / 2: return left
    if rightCount > len(array) / 2: return right
    # if neither wins, just return None
    return None

上述算法是O(nlogn)时间但只有O(logn)空间。

如果你想找到一个数组的模式(不仅仅是主模式),首先计算直方图。您可以在 O(n) 时间内完成此操作(仅访问数组的每个元素一次),方法是将直方图存储在将元素值映射到其频率的哈希表中。

计算出直方图后,您可以对其进行迭代(最多访问每个元素一次)以找到最高频率。一旦发现频率大于数组大小的一半,您可以立即返回并忽略直方图的其余部分。由于直方图的大小不能大于原始数组的大小,所以这一步也是O(n)时间(和O(n)空间)。

由于这两个步骤都是 O(n) 时间,因此产生的算法复杂度是 O(n) 时间。

【讨论】:

  • @mike_b:Big-O 符号只是给出了一个上限;所有 O(n) 函数也是 O(nlogn)。也就是说,我给出的算法也有 O(nlogn) 复杂度。
  • 几乎。 1. 你需要检查 left==none 或 right==none 的情况 2. 你可以在原地运行它,所以在空间中 O(1)。
  • @thang:如果left==Noneright==None,我需要做些什么不同的事情?而且我看不出如何在不使用 k*log(n) 堆栈空间的情况下深度递归 log(n)。
  • 讲师暗示我们需要在没有简单线性搜索的情况下进行。他不想要 O(n) 的东西。他想要一个递归调用的函数,类似于归并排序。您现在拥有的与我开发的类似。您的右侧也有重叠,应该是 len(array)/2+1。您的计数是否仅适用于该函数的该实例?如果是这样,有时这不起作用,需要在递归调用中维护一个值的计数。
  • @Gabe,您实际上可以纠正它,但您是对的。我犯了一个错误。在该代码中,您确实需要在堆栈空间中登录 n。关于左右,我又查了一遍,你暗中处理了。很酷。 +1。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-06-30
  • 2012-03-08
  • 2016-03-02
  • 2017-02-08
  • 2019-12-12
  • 2021-09-28
  • 1970-01-01
相关资源
最近更新 更多