【问题标题】:How to modify my method to search and then remove duplicates in O(N) or O(N * log N)?如何修改我的方法以搜索然后删除 O(N) 或 O(N * log N) 中的重复项?
【发布时间】:2012-10-09 18:30:16
【问题描述】:

我创建了一个搜索重复项然后将重复项索引存储到另一个数组中的方法。然后我正在遍历我的大数组并移动所有条目而不重复。

现在,我的问题是它使用 O(N*N) 并且我正在使用额外的内存空间,因为我正在添加额外的数组。

如何做到这一点? 假设我需要了解如何在不使用其他库或 HashSet 的情况下完成此操作。

感谢任何提示。

   public void dups()
   {
       int[] index = new int[100];

       int k = 0;
       int n = 0;
       int p = 0;

       for (int i = 0; i < elements; i++)
           for (int j = i + 1; j < elements; j++)
               if(a[j].equals(a[i]))
                   index[k++] = i;

       for (int m = 0; m < elements; m++)
           if (m != index[p])
               a[n++] = (T) a[m];
           else
               p++;

       elements -= k;
   }

【问题讨论】:

  • 在 O(N) 中删除重复项是不可能的。
  • 他没有说不要使用哈希表。
  • 不要分裂头发,但 O(n) 可以使用 HashMap 完成。但我不确定“无 HashSet”要求是否包括 HashMap。

标签: java arrays duplicates time-complexity


【解决方案1】:

您在O(n) 中找不到重复项(通常)。

但是在 O(n*log n) 中是可能的。只需对您的数组 (O(n*log n)) 进行排序,然后可以在 O(n) 中完成对重复项的扫描。

另一方面,如果您可以使用哈希表(如果您不想使用任何其他库,您可能不想这样做),您可以扫描数组并计算每个元素出现的频率在数组中。之后,您可以遍历哈希表中的每个元素,并找到那些出现多次的元素。这将需要 预期的 O(n) 运行时,但不是确定性的 O(n)

最后,为什么我写了你在O(n) 中通常找不到重复项?
可以想象几种特殊情况,在O(n) 中可以找到重复项。 例如,您的数组只能包含从 0 到 99 的数字。 在这种情况下,您可以使用另一个数组(大小为 100)来计算每个元素在数组中出现的频率。这与哈希表的工作方式相同,但其运行时间将是确定性的O(n)

另一个可以在O(n) 中查找重复项的示例当然是,如果数组已经排序。

【讨论】:

    【解决方案2】:

    使用HashSet 在 O(n) 时间内完成此操作:

    public <T> int removeDups(T[] original) {
        HashSet<T> unique = new HashSet<T>();
        for (T item: original) {
            unique.add(item);
        }
    
        int size = unique.size();
        int curr = 0;
        for (int i = 0; i < original.length; i += 1) {
            if (unique.remove(original[i])) {
                original[curr] = original[i];
                curr++;
            }
        }
    
        return size;
    }
    

    请注意,这取决于列表元素的 hashCode 方法是否将元素正确分布在 HashSet 中的存储桶上以实现 O(n)。在最坏的情况下,这是 O(n*m),其中 m 是唯一元素的数量,所以你一定要测量它。

    此实现修改数组,并返回唯一元素的数量。尽管数组可能比这个大,超过那个点的元素应该被认为是垃圾。

    它会遍历列表以将项目添加到HashSet(添加项目是 O(1)),然后是更新数组,所以它是 O(n)(再次假设一个好的哈希函数)。

    【讨论】:

    • 这不是 O(n),而是 O(n) expected (因为散列在恒定 expected 时间中运行,而不是恒定时间) .
    • 我可能不应该通过创建另一个 ArrayList 来让我的程序使用额外的内存。
    • @HelpNeeder -- 这听起来像premature optimization
    • @leemes 在这种情况下,预期的 O(n) 可能非常好,并且可以轻松地改进 O(n^2) 或 O(n*log n)。
    • @Brigham 哈希表的最坏情况比 O(nlog n) 更糟糕。 *不可能在确定的线性时间内找到重复项,这就是我想强调的。
    【解决方案3】:
    array1 = [1,2,2,3,3,3,4,5,6,4,4,5,5,5,5,10,10,8,7,7,9,10]
    
    array1.sort() # sorting is must
    print(array1)
    
    current = NONE
    count = 0 
    
    # overwriting the numbers at the frontal part of the array
    for item in array1:
        if item != current:
            array1[count] = item
            count +=1
            current=item
            
           
    
    print(array1)#[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 5, 5, 5, 5, 6, 7, 7, 8, 9, 10, 10, 10]
    
    print(array1[:count])#[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
    

    最有效的方法是:

    array1 = [1,2,2,3,3,3,4,5,6,4,4,5,5,5,5,10,10,8,7,7,9,10]
    
    array1.sort()
    print(array1)
    
    print([*dict.fromkeys(array1)])#[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
    
    #OR#
    aa = list(dict.fromkeys(array1))
    print( aa)#[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
    

    【讨论】:

      【解决方案4】:

      这不是 O(n) 因为哈希和等于比较,并使用 LinkedHashSet,它是 Java 标准库的一部分,但可能足够接近:

      public void dups() {
          Set<Integer> uniques = new LinkedHashSet<>();
          for (int i = 0; i < elements.length; i++) {
              uniques.add(elements[i]);
          }
          // todo: copy the set into a list, then call toArray() to get an array.
      }
      

      【讨论】:

        【解决方案5】:

        HashMap 的默认实现是基于数组的并且是 O(n)。因此,如果您想要一个有趣的练习,您可以筛选 HashMap 的实现,以准确了解它如何散列其键。基本上,它使用键的 hashCode 并使用它来索引预定位置的数组(hashCode & arraylength - 1),并将值存储在该索引处。如果您要重复这个概念,同时使用值作为键和值,那么您的数组中将只有唯一的条目。

        但是,如果您有大量重复项,但只有唯一值,您最终会得到一个包含许多空槽的数组。填充数组后,您只需循环一次即可删除任何空槽。 (例如:将所有非空条目复制到列表中)

        这将是 O(n),但需要 2 次通过 - 一次填充数组,一次删除空槽。它还需要一个与现有数组长度相同的附加数组,以及一个较小的数组(或列表)来获得唯一值的最终列表。

        【讨论】:

          猜你喜欢
          • 2011-12-12
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-06-13
          • 1970-01-01
          • 1970-01-01
          • 2011-12-11
          • 1970-01-01
          相关资源
          最近更新 更多