【问题标题】:Find the element repeated more than n/2 times找到重复 n/2 次以上的元素
【发布时间】:2011-08-14 21:16:56
【问题描述】:

有一个数组(大小为 N),其中一个元素重复超过 N/2 次,数组中的其余元素也可以重复,但只有一个元素重复超过 N/2 次。找到号码。

我能想到几种方法:

  • 天真,将每个数字的计数保存在哈希图中。
  • 最简单,对数组进行排序,第 n/2+1 个索引处的数字是 必填号码。
  • 只保留找到的连续重复值的计数。查看 分别用于交替存储值的模式。

想不出更好的解决方案,必须有。

【问题讨论】:

  • 来自 WIKI:通过将所有观察值从最低值到最高值排列并选择中间值,可以找到有限数字列表的中位数。基本上排序并给出你的号码:)
  • 你的速度不能超过 O(N)。排序是多余的。

标签: arrays algorithm


【解决方案1】:

有一个漂亮的算法可以解决这个问题,它只使用恒定的外部空间 (O(1)) 分两次运行(总时间 O(N))。我有这个算法的实现,以及包括正确性证明的 cmets,available here

算法背后的直觉其实很漂亮。假设你有一屋子的人,每个人都拿着数组的一个元素。每当两个人发现彼此都没有持有相同的数组元素时,他们两个就坐下来。最终,在最后,如果有人站着,他们有可能占多数,你可以检查那个元素。只要一个元素以至少 N/2 的频率出现,您就可以保证这种方法总能找到多数元素。

要实际实现该算法,您需要对数组进行线性扫描,并跟踪您当前对多数元素的猜测,以及到目前为止您看到它的次数。最初,这个猜测是未定义的,重复次数为零。当您遍历数组时,如果当前元素与您的猜测相符,则递增计数器。如果当前元素与您的猜测不符,则递减计数器。如果计数器达到零,则将其重置为遇到的下一个元素。您可以将此实现视为上述“站在房间里”算法的具体实现。每当两个人遇到不同的元素时,他们就会抵消(放下计数器)。每当两个人有相同的元素时,他们就不会相互交流。

要获得完整的正确性证明,请参阅原始论文(Boyer 和 Moore 对更著名的 Boyer-Moore 字符串匹配算法的引用)以及 C++ 中的实现,请查看上面的链接。

【讨论】:

  • 你博客上有趣的代码部分非常好,另一个很好的解释+1。
  • 有人能帮我理解当计数器归零时重置计数器是什么吗?这一步有什么用?如果我们不这样做会发生什么。
【解决方案2】:

这是多数元素问题。 对于这个问题,有一个单程、恒定空间算法。 这是一个用python编码的简短算法:


    import random

    items = [1, 2, 3, 4, 5, 5, 5, 5, 5 ]
    # shuffle the items
    random.shuffle(items)

    print("shuffled items: ", items)

    majority_elem = items[0]
    count = 1
    for i in range(1,len(items)):
        if items[i] == majority_elem:
            count += 1
        else: 
            count -= 1
            if count == 0:
                majority_elem = items[i]
                count = 1

    print("majority element : %d" % majority_elem )

  

我们使用一个变量majority_elem 来跟踪多数元素和一个计数器(计数)

  • 最初我们将数组的第一个元素设置为多数元素。

  • 我们在数组中导航,

  • 如果当前元素 == 多数元素:递增计数

  • else : { 递减计数。如果 count 变为零,则设置 count = 1 并设置 major_element = 当前元素。 }

这个问题有一个变体,不是数组,而是一个非常大的序列,我们事先不知道长度。如果这种情况,排序或分区没有帮助。

参考资料:

  • 计算机编程艺术,分册 0:组合算法和布尔函数简介,第 0 卷;第 4 卷

【讨论】:

  • 非常优雅的实现+1
  • 不,这不是多数元素问题。此算法在数组包含 2 个或多于 2 个数字的情况下失败,重复 n/k 次。
  • 哦,抱歉,没仔细看。
  • 如果没有多数怎么办?返回最后一个元素
  • 很抱歉,您的算法不正确。您需要通过第二次遍历数组来确认您找到的元素实际上是大多数元素。我同意这个问题表明数组中有一个数字,重复超过 N/2 次。您至少应该将其作为必要的先决条件添加到您的答案中,否则答案会产生误导。
【解决方案3】:

如果一个元素重复 N/2 次以上,那么它一定是中位数。有many algorithms 可以让您高效地找到它。

【讨论】:

    【解决方案4】:

    你熟悉快速排序吗?它有一个名为“分区”的函数,给定一个值,将数组划分为一个部分,其中所有值大于该值(枢轴)位于一侧,而所有小于该值的值位于另一侧。请注意,这不是一种排序,只是一种分离。 N/2 计数项目将位于两个部分中较大的部分。您可以递归应用此技术以在 O(n) 时间内找到元素。

    维基百科:快速排序,或基于分区的通用选择算法

    【讨论】:

      【解决方案5】:

      在您的第二种方法中,您实际上是在选择中间元素。看一下查找数字列表中位数的算法。特别是,selection algorithm 可以很好地解决这个问题并在 O(n) 中计算它。

      Hoare 的选择算法的工作原理与快速排序非常相似,不同之处在于它不是向下递归两个分区,而是仅向下递归一个分区(包含第 k 个元素的分区)。

      在C++中,标准库提供了std::nth_element形式的选择算法,保证了O(n)的平均复杂度。您可以使用它找到中位数。

      int a[8] = {5, 1, 1, 1, 2, 1, 3, 1};
      int median = *std::nth_element(a, a + 4, a + 8);
      

      请注意,std::nth_element 也会对数组进行部分就地排序。

      【讨论】:

        【解决方案6】:

        无需排序。您可以简单地使用中值选择算法来确定第 n/2 个元素。快速选择在 O(n) 预期时间内运行。中位数的中位数在 O(n) 内运行。

        【讨论】:

        • 抱歉,是指快速选择。修正了错字。
        【解决方案7】:

        使用任何排序算法对数组进行排序。重复超过一半时间的元素将始终是中间元素。复杂度将为 nlog(n)。

        【讨论】:

          猜你喜欢
          • 2011-04-14
          • 1970-01-01
          • 2020-02-17
          • 2014-08-05
          • 1970-01-01
          • 2018-08-11
          • 1970-01-01
          • 2011-03-01
          • 1970-01-01
          相关资源
          最近更新 更多