【问题标题】:Remove duplicates from Array without using Hash Table在不使用哈希表的情况下从数组中删除重复项
【发布时间】:2011-05-22 16:38:35
【问题描述】:

我有一个可能包含重复元素的数组(一个元素的两个以上重复)。我想知道是否可以找到并删除数组中的重复项:

  • 不使用哈希表(严格要求)
  • 不使用临时辅助阵列。对复杂性没有限制。

P.S这不是家庭作业问题

在雅虎技术面试中被问到我的朋友

【问题讨论】:

  • 尽管“对复杂性没有限制”,但我不会亲自雇用任何对此做出O(n^2) 回复的人:P
  • @Billy:我认为候选人的正确态度是解释权衡:就地排序破坏了原始顺序但满足了直接的功能需求,而 O(N^2)对于较大的 N,可以预期会变慢,但可以保持顺序。从绝对意义上讲,这两个答案都不一定更好,至少当问题特意说对复杂性没有限制时。
  • @Tony:如果您需要保持顺序,您可以随时按元素的原始位置重新排序目标数组,同时仍然避免二次复杂性。
  • @Billy:你可以吗?你怎么知道他们原来的位置?您不允许使用临时辅助阵列来记录它们。它们的顺序可能与它们内部的任何数据无关,甚至可能由程序中其他地方的数据隐含。
  • @Tony:为什么不呢? :P (我知道面试题说源码不能复制,但面试题并没有加顺序要求)在现实世界的程序中,最好花小空间开销,节省二次复杂度。

标签: arrays algorithm duplicates


【解决方案1】:

对源数组进行排序。查找 连续 个相等的元素。 (即 std::unique 在 C++ 领域所做的事情)。总复杂度为 N lg N,如果输入已经排序,则仅为 N。

要删除重复项,您可以将数组后面的元素复制到数组前面的元素上,也可以在线性时间内进行。只需保留一个指向容器新逻辑端的指针,并在每一步将下一个不同的元素复制到新的逻辑端。 (再一次,就像std::unique 所做的那样(事实上,为什么不直接下载 an implementation of std::unique 并完全按照它的方式做呢?:P))

【讨论】:

    【解决方案2】:

    O(NlogN) : 对连续的相同元素进行排序和替换。

    O(N2) :运行嵌套循环将每个元素与数组中的剩余元素进行比较,如果发现重复,则将重复项与数组末尾的元素交换并减少数组大小减 1。

    【讨论】:

    • 如何检查连续元素是否相等?
    • 有几秒钟,我以为您在列举步骤而不是替代方案...看到您的帖子太多以至于不相信这样的错误,所以一直在困惑:-)
    【解决方案3】:

    对复杂性没有限制。

    所以这是小菜一碟。

    // A[1], A[2], A[3], ... A[i], ... A[n]
    
    // O(n^2)
    for(i=2; i<=n; i++)
    {
        duplicate = false;
        for(j=1; j<i; j++)
            if(A[i] == A[j])
                 {duplicate = true; break;}
        if(duplicate)
        {
            // "remove" A[i] by moving all elements from its left over it
            for(j=i; j<n; j++)
                A[j] = A[j+1];
            n--;
        }
    }
    

    【讨论】:

    • 你的代码很好,但这是一个面试问题,面试官更喜欢 O(NlogN) 而不是 O(N^2)。
    • 好吧,没有限制,但是有两个选项,你有一个更好的选择,我的朋友给了他 O(N^2) 的解决方案,等待面试结果..
    【解决方案4】:

    在二次时间中保留列表现有顺序的就地重复删除:

    for (var i = 0; i < list.length; i++) {
      for (var j = i + 1; j < list.length;) {
        if (list[i] == list[j]) {
          list.splice(j, 1);
        } else {
          j++;
        }
      }
    }
    

    诀窍是在i + 1 上启动内部循环,并且在删除元素时不增加内部计数器。

    代码是 JavaScript,splice(x, 1) 删除 x 处的元素。

    如果订单保留不是问题,那么您可以更快地做到这一点:

    list.sort();
    
    for (var i = 1; i < list.length;) {
      if (list[i] == list[i - 1]) {
        list.splice(i, 1);
      } else {
        i++;
      }
    }
    

    这是线性的,除非你计算排序,这是你应该计算的,所以它是排序的顺序——在大多数情况下是 n × log(n)。

    【讨论】:

      【解决方案5】:

      在函数式语言中,您可以一次性将排序和统一(这是一个真实的词吗?)结合起来。 让我们采用标准的快速排序算法:

      - Take the first element of the input (x) and the remaining elements (xs)
      - Make two new lists
      - left: all elements in xs smaller than or equal to x
      - right: all elements in xs larger than x
      - apply quick sort on the left and right lists
      - return the concatenation of the left list, x, and the right list
      - P.S. quick sort on an empty list is an empty list (don't forget base case!)
      

      如果您只想要唯一的条目,请替换

      left: all elements in xs smaller than or equal to x

      left: all elements in xs smaller than x

      这是一个一次性 O(n log n) 算法。

      F# 中的示例实现:

      let rec qsort = function
          | [] -> []
          | x::xs -> let left,right = List.partition (fun el -> el <= x) xs
                     qsort left @ [x] @ qsort right
      
      let rec qsortu = function
          | [] -> []
          | x::xs -> let left = List.filter (fun el -> el < x) xs
                     let right = List.filter (fun el -> el > x) xs
                     qsortu left @ [x] @ qsortu right
      

      以及交互模式下的测试:

      > qsortu [42;42;42;42;42];;
      val it : int list = [42]
      > qsortu [5;4;4;3;3;3;2;2;2;2;1];;
      val it : int list = [1; 2; 3; 4; 5]
      > qsortu [3;1;4;1;5;9;2;6;5;3;5;8;9];;
      val it : int list = [1; 2; 3; 4; 5; 6; 8; 9]
      

      【讨论】:

      • 这忽略了“没有...临时辅助阵列”的要求。
      【解决方案6】:

      由于这是一个面试问题,面试官通常希望被问及问题的精确度。

      由于不允许替代存储(即允许 O(1) 存储,因为您可能会使用一些计数器/指针),显然预期会发生破坏性操作,可能值得向面试官指出.

      现在真正的问题是:你想保留元素的相对顺序吗?即这个操作应该是稳定的吗?

      稳定性极大地影响了可用的算法(因此也影响了复杂性)。

      最明显的选择是列出Sorting Algorithms,毕竟数据一旦排序,很容易得到唯一的元素。

      但是如果你想要稳定性,你实际上不能对数据进行排序(因为你无法得到“正确”的顺序),因此我想知道如果涉及稳定性,它是否可以在小于 O(N**2) 的时间内解决。

      【讨论】:

        【解决方案7】:

        本身不使用哈希表,但我知道在幕后它是一个哈希表的实现。不过,我想我可能会发布以防万一。这是在 JavaScript 中并使用关联数组来记录要传递的重复项

        function removeDuplicates(arr) {
            var results = [], dups = []; 
        
            for (var i = 0; i < arr.length; i++) {
        
                // check if not a duplicate
                if (dups[arr[i]] === undefined) {
        
                    // save for next check to indicate duplicate
                    dups[arr[i]] = 1; 
        
                    // is unique. append to output array
                    results.push(arr[i]);
                }
            }
        
            return results;
        }
        

        【讨论】:

          猜你喜欢
          • 2015-06-02
          • 1970-01-01
          • 2018-07-27
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-07-31
          • 1970-01-01
          相关资源
          最近更新 更多