Find a duplicate in array of integers

时间:2018-02-17 12:46:47

标签: arrays algorithm time-complexity big-o space-complexity

This was an interview question.

I was given an array of n+1 integers from the range [1,n]. The property of the array is that it has k (k>=1) duplicates, and each duplicate can appear more than twice. The task was to find an element of the array that occurs more than once in the best possible time and space complexity.

After significant struggling, I proudly came up with O(nlogn) solution that takes O(1) space. My idea was to divide range [1,n-1] into two halves and determine which of two halves contains more elements from the input array (I was using Pigeonhole principle). The algorithm continues recursively until it reaches the interval [X,X] where X occurs twice and that is a duplicate.

The interviewer was satisfied, but then he told me that there exists O(n) solution with constant space. He generously offered few hints (something related to permutations?), but I had no idea how to come up with such solution. Assuming that he wasn't lying, can anyone offer guidelines? I have searched SO and found few (easier) variations of this problem, but not this specific one. Thank you.

EDIT: In order to make things even more complicated, interviewer mentioned that the input array should not be modified.

4 个答案:

答案 0 :(得分:12)

  1. 取最后一个元素(x)。

  2. 将元素保存在x(y)位置。

  3. 如果x == y,则发现重复。

  4. 用x。覆盖位置x。

  5. 指定x = y并继续执行步骤2.

  6. 您基本上是对数组进行排序,因为您知道元素必须插入的位置。 O(1)额外空间和O(n)时间复杂度。你只需要小心索引,为简单起见我假设第一个索引是1(不是0)所以我们不必做+1或-1。

    编辑:不修改输入数组

    该算法基于我们必须找到置换周期的入口点的想法,然后我们还发现了一个重复的(为简单起见,再次使用基于1的数组):

    示例:

    2 3 4 1 5 4 6 7 8
    

    参赛作品:8 7 6

    置换周期:4 1 2 3

    我们可以看到副本(4)是循环的第一个数字。

    1. 查找排列周期

      1. x =最后一个元素
      2. x =位置x的元素
      3. 重复步骤2. n次(总计),这可以保证我们进入周期
    2. 测量周期长度

      1. a =上面的最后x,b =上面的最后x,计数器c = 0
      2. a =位置a处的元素,b =位置b处的元素,b =位置b处的元素,c ++(因此我们使用b向前迈出2步,在循环中向前迈出1步)
      3. 如果a == b,周期长度为c,否则继续步骤2.
    3. 查找循环的入口点

      1. x =最后一个元素
      2. x =位置x的元素
      3. 重复步骤2.c次(总计)
      4. y =最后一个元素
      5. 如果x == y那么x就是一个解(x做了一个完整周期,y就要进入周期)
      6. x =位置x处的元素,y =位置y处的元素
      7. 重复步骤5.和6.直到找到解决方案。
    4. 3个主要步骤都是O(n)和顺序因此总体复杂度也是O(n),空间复杂度是O(1)。

      上面的例子:

      1. x取以下值:8 7 6 4 1 2 3 4 1 2

      2. a取以下值:2 3 4 1 2
        b取以下值:2 4 2 4 2
        因此c = 4(是的,有5个数字,但c只在步骤时增加,而不是最初)

      3. x取以下值:8 7 6 4 | 1 2 3 4
        y采用以下值:| 8 7 6 4
        x == y == 4最后,这是一个解决方案!

      4. 评论中要求的示例2:3 1 4 6 1 2 5

        1. 输入周期:5 1 3 4 6 2 1 3

        2. 测量周期长度:
          a:3 4 6 2 1 3
          b:3 6 1 4 2 3
          c = 5

        3. 找到切入点:
          x:5 1 3 4 6 | 2 1
          y:| 5 1
          x == y == 1是一个解决方案

答案 1 :(得分:5)

这是一个可能的实现:



function checkDuplicate(arr) {
  console.log(arr.join(", "));
  let  len = arr.length
      ,pos = 0
      ,done = 0
      ,cur = arr[0]
      ;
  while (done < len) {
    if (pos === cur) {
      cur = arr[++pos];
    } else {
      pos = cur;
      if (arr[pos] === cur) {
        console.log(`> duplicate is ${cur}`);
        return cur;
      }
      cur = arr[pos];
    }
    done++;
  }
  console.log("> no duplicate");
  return -1;
}

for (t of [
     [0, 1, 2, 3]
    ,[0, 1, 2, 1]
    ,[1, 0, 2, 3]
    ,[1, 1, 0, 2, 4]
  ]) checkDuplicate(t);
&#13;
&#13;
&#13;

它基本上是@maraca提出的解决方案(输入太慢了!)它具有恒定的空间要求(对于局部变量),但除此之外只使用原始数组进行存储。在最坏的情况下应该是O(n),因为一旦找到重复,该过程就会终止。

答案 2 :(得分:2)

如果允许非破坏性地修改输入向量,那么这很容易。假设我们可以通过否定它来标记输入中的元素(这显然是可逆的)。在这种情况下,我们可以按如下方式进行:

注意:以下假设向量从1开始编制索引。因为它可能从0开始索引(在大多数语言中),所以可以实现“索引i处的标记项”和“否定项目” index i-1“。

  1. 将i设置为0并执行以下循环:
    1. 增加i直到第i项未标记。
    2. 将j设置为i并执行以下循环:
      1. 将j设置为vector [j]。
      2. 如果j处的项目被标记,则j是重复的。终止两个循环。
      3. 在j。
      4. 标记该项目
      5. 如果j!= i,继续内循环。
  2. 遍历将每个元素设置为其绝对值的向量(即,取消所有内容以恢复向量)。

答案 3 :(得分:0)

  • 这取决于您(您的应用)可以使用的工具。目前存在许多框架/库。对于C ++标准版的例子,您可以使用std :: map&lt;&gt; ,正如马拉卡所说。

  • 或者如果你有时间可以自己实现二叉树,但是你需要记住,元素的插入与通常的数组相同。在这种情况下,您可以在特定情况下优化对重复项的搜索。

二叉树探索。参考: https://www.wikiwand.com/en/Binary_tree