【问题标题】:Find unique common element from 3 arrays从 3 个数组中查找唯一的公共元素
【发布时间】:2010-03-10 15:52:42
【问题描述】:

原问题:
我有 3 个盒子,每个盒子里有 200 个硬币,因为只有一个人从三个盒子里打了电话,因此每个盒子里有一枚硬币,指纹相同,其余硬币指纹不同。您必须从所有 3 个盒子中找到包含相同指纹的硬币。这样我们就可以从三个盒子中找到打电话的人的指纹。

转换后的问题:
您有 3 个数组,每个数组包含 200 个整数。假设这 3 个数组中只有一个公共元素。找到共同的元素。
请考虑解决这个问题,而不是简单的 O(1) 空间和 O(n^3) 时间。

【问题讨论】:

  • 您能否澄清其中一个数组是否可以具有重复元素?例如,你能有A = {1, 1, 1, 1, 1}; B = {2, 3, 4, 5, 1}; C = {2, 2, 2, 2, 1};吗?
  • 请仔细阅读qn。
  • 另外,你说三个数组中只有一个共同元素。这是否意味着所有三个数组中只有一个元素出现,并且三个数组中只有两个可以具有其他共同元素,或者三个数组中的两个除了三个数组中的共同元素之外没有其他共同元素吗?
  • @Rajendra:我已经仔细阅读了,无论是原问题还是转换问题都可以有多种解释方式。回答是或否有那么难吗?
  • @Rajendra 感谢您的回答,这使问题变得非常简单。但是,我仍然认为您看到了不存在的东西。 “鉴于这 3 个数组中只有一个公共元素”意味着存在一个在 ALL 3 中出现的单个元素。它没有说明一个元素不能只出现在三个数组中的两个中,或者一个元素不能在一个数组中出现 199 次。但无论如何,我的解决方案忽略了这一点,只是考虑了你的“否”答案。祝你面试顺利。

标签: algorithm


【解决方案1】:

Pelkonen 的回答有所改进:
来自 OP 中的转换问题:

“鉴于这 3 个数组中只有一个公共元素。”

我们只需要对 2 个数组进行排序并找到共同的元素。

【讨论】:

  • +1 你需要遍历所有 1 个数组,所以你不需要对那个数组进行排序
  • 数组A和B有可能共享共同的元素(a, b);数组 B 和数组 C 共享共同的元素 (b, c)。放在一起,数组 A、B 和 C 共享共同的元素 b。因此,仅对 2 个数组进行排序是不够的。
【解决方案2】:

如果你首先对所有数组进行排序 O(n log n),那么在不到 O(n^3) 的时间内就很容易找到公共元素。例如,您可以在对它们进行排序后使用二进制搜索。

【讨论】:

  • 或者只是将它们合并排序成一个大数组并检查一式三份的条目。
  • 回答我自己的评论:原始问题不允许重复,但转换后的问题没有说明整数的唯一性
  • 您实际上并没有合并它们,您只是使用类似于合并排序的算法并在所有 3 个输入具有相同值时停止。
【解决方案3】:

设 N = 200,k = 3,

  1. 创建容量≥Nk的哈希表H。

  2. 对于数组 1 中的每个元素 X,将 H[X] 设置为 1。

  3. 对于数组 2 中的每个元素 Y,如果 Y 在 H 并且 H[Y] == 1,则设置 H[Y] = 2。

  4. 对于数组 3 中的每个元素 Z,如果 Z 在 H 并且 H[Z] == 2,则返回 Z。

  5. throw new InvalidDataGivenByInterviewerException();

O(Nk) 时间,O(Nk) 空间复杂度。

【讨论】:

    【解决方案4】:

    为每个整数使用哈希表并对条目进行编码,以便您知道它来自哪个数组 - 然后检查包含所有 3 个数组条目的插槽。 O(n)

    【讨论】:

      【解决方案5】:

      使用哈希表将对象映射到频率计数。遍历所有三个列表,递增哈希表中的出现次数,直到遇到出现次数为 3 的列表。这是 O(n),因为不需要排序。 Python 中的示例:

      def find_duplicates(*lists):
        num_lists = len(lists)
        counts = {}
        for l in lists:
          for i in l:
            counts[i] = counts.get(i, 0) + 1
            if counts[i] == num_lists:
              return i
      

      或等效的,使用集合:

      def find_duplicates(*lists):
        intersection = set(lists[0])
        for l in lists[1:]:
          intersection = intersection.intersect(set(l))
        return intersection.pop()
      

      【讨论】:

      • +1:我打算提出类似的建议。你打败了我!
      【解决方案6】:

      O(N) 解决方案:使用哈希表。 H[i] = 映射到 i 的三个数组中所有整数的列表。

      对于所有 H[i] > 1,检查其三个值是否相同。如果是,你有你的解决方案。你甚至可以用简单的解决方案来做这个检查,它应该仍然非常快,或者你可以对那些 H[i] 进行排序,然后它就变得微不足道了。

      如果你的数字比较小,你可以使用 H[i] = k 如果 i 在三个数组中出现 k 次,那么解决方案是 H[i] = 3 的 i。如果你的数字很大,不过使用哈希表。

      即使您可以拥有仅对两个数组通用的元素,并且如果您可以在其中一个数组中拥有重复元素的元素,您也可以将其扩展为工作。它只是变得有点复杂,但你应该能够自己解决。

      【讨论】:

        【解决方案7】:

        如果您想要最快的*答案:

        • 对一个数组进行排序——时间为 N log N。
        • 对于第二个数组中的每个元素,搜索第一个。如果找到它,将 1 添加到伴随数组;否则加 0--time 为 N log N,使用 N 个空间。
        • 对于每个非零计数,将相应的条目复制到临时数组中,并对其进行压缩以使其仍处于排序状态——时间为 N。
        • 对于第三个数组中的每个元素,搜索临时数组;当你发现命中时,停止。时间小于 N log N。

        以下是 Scala 中的代码说明了这一点:

        import java.util.Arrays
        
        val a = Array(1,5,2,3,14,1,7)
        val b = Array(3,9,14,4,2,2,4)
        val c = Array(1,9,11,6,8,3,1)
        
        Arrays.sort(a)
        val count = new Array[Int](a.length)
        for (i <- 0 until b.length) {
          val j =Arrays.binarySearch(a,b(i))
          if (j >= 0) count(j) += 1
        }
        var n = 0
        for (i <- 0 until count.length) if (count(i)>0) { count(n) = a(i); n+= 1 }
        for (i <- 0 until c.length) {
          if (Arrays.binarySearch(count,0,n,c(i))>=0) println(c(i))
        }
        

        稍微复杂一点,您既可以不使用额外空间,但对原始数组的破坏性更大,也可以完全避免接触原始数组,代价是另外 N 个空间。


        编辑:* 正如 cmets 所指出的,哈希表对于非反常输入更快。这是“最快最坏的情况”。除非您使用非常好的散列算法,否则最坏的情况可能不会那么不可能,这可能会比您的排序消耗更多的时间。例如,如果你将所有值乘以 2^16,那么在小于 64k 的列表上,微不足道的散列(即仅使用位掩码整数作为索引)每次都会发生冲突......

        【讨论】:

        • 这比哈希表解决方案快多少?
        • @lVlad:你不能保证哈希表查找是 O(1)。根据散列函数,实现方式如何。处理碰撞和实际输入,那么您的性能可能会恶化到 O(n)。
        • @Mads Ravn:是的,你不能保证,但考虑到这个问题的实际要求,你可以非常接近保证。阅读 OP 帖子的 cmets。在这些限制下,我认为强制哈希函数进入最坏情况模式将非常困难。
        • @lVlad:你是对的。如果整数是不同的(除了我们正在搜索的那个),或者是从一大组具有统一概率的潜在数字中选择的,那么确实很难想象一个“现实的”哈希表 impl。恶化。我只是有一个讨厌的习惯,每当我看到一个大的哦-该死的 CLRS 时,我都会思考理论上的最坏情况 :) 作为一个 CS 教授的旁白,曾经开玩笑地告诉我:“如果你希望你的程序在实践中表现良好,然后你使用哈希表。如果你想让你的程序在理论上表现良好,那么你使用平衡树。
        • 最快的最坏情况解决方案,我的意思是(假设排序是使用合并排序等完成的)。我同意哈希表可以更快,尤其是在内存没有问题的情况下。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-07-20
        • 1970-01-01
        • 2015-03-07
        • 1970-01-01
        相关资源
        最近更新 更多