如何修改我的方法来搜索,然后删除O(N)或O(N * log N)中的重复项?

时间:2012-10-09 18:30:17

标签: java arrays duplicates time-complexity

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

现在,我的问题是它使用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;
   }

4 个答案:

答案 0 :(得分:4)

您无法在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)中查找重复项的另一个例子当然是,如果数组已经排序。

答案 1 :(得分:1)

使用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)(再次,假设一个良好的哈希函数)。

答案 2 :(得分:0)

由于hash和equals比较,这不是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.
}

答案 3 :(得分:0)

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

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

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