在O(N)时间内查找数组中的重复项

Find Duplicates in an array in O(N) time

本文关键字:数组 查找 时间      更新时间:2023-10-16

是否有一种方法可以在O(N)时间内找到N个元素数组中的所有重复元素?

的例子:

输入:11, 29, 81, 14, 43, 43, 81, 29

输出:29, 81, 43

对输入进行排序并进行线性扫描以检测重复项,破坏了顺序并给出输出:29,43,81。

根据给定数组对另一个索引数组{0,1,...N-1}按键排序得到{1,4,2},然后对结果索引集排序得到{1,2,4},将得到{29,81,43},但这需要O(N logN)时间。

是否存在O(N)算法来解决这个问题?

注:我忘了补充:我不想用哈希表。我正在寻找一个非哈希解决方案。

我认为一个好的解决方案(体面的内存使用,可以用来立即确定条目是否已经被看到,从而保持顺序,并具有线性复杂性)是一个trie。

如果您将元素插入到树中,就好像它们是每个节点中每个数字(从MSD开始)的字符串一样,您可以以0 (m N)的复杂度完成此操作,其中m是以10为基数的数字的平均长度。

你只需要循环遍历所有条目并将它们插入到树中。每当一个元素已经存在时,就跳过它,继续下一个元素。这里的重复项(不像我之前对基数排序的回答)将立即找到,而不是在最后一次迭代中找到。

我不确定您是否会从这里使用后缀树中受益,因为输入到树中的字符的"基数"只有10(与ANSI字符串的基数128相比),但这是可能的。

如果你的输入都是小整数,你可以使用计数排序,它在O(n)时间内运行,需要O(m)空间,其中m是可能输入范围的大小。

作为空间优化,使用位数组并使用单个位(而不是计数)来存储您之前是否见过该项就足够了。

听起来您反对分配任何额外的空间。尽管如此,哈希表仍然是提高速度的正确解决方案。老实说,大多数用于简单数据(如整数)的哈希表实现都过于单一解决方案,以至于我只能根据自己的需要自行制定。当你需要它做一些相对较少的工作时,它可以把慢代码变成快代码。

同样,如果你反对哈希表是因为它们破坏了顺序,那么你可能想要稍微不同地使用它们来获得期望的O(n),同时保持顺序:

创建一个哈希表,将数组元素映射为2位,作为从0到3的计数字段,并将30位作为元素数组的索引。除非你的数组中有超过10亿个值,否则30位就足够了。这样你的哈希值就是一个32位的单词。

遍历数组中的元素。如果某个元素不在表中,则将该值插入哈希表并将count字段设置为零。当你存储它的时候,索引部分是什么并不重要。如果元素在表中,并且count字段为零,则将其提高到1,并使用新的count字段值存储元素索引。如果count字段已经是1或更大,则将其设置为2,并且不要触及存储的索引—保持它不变。

再次遍历数组中的元素。查找每个元素,如果它的索引是存储的,并且相关的count字段大于零,则将其打印出来。

这将在O(n)时间内以适当的顺序产生您想要的结果。但是,由于未知的原因,它使用了不需要的哈希表。我强烈建议您要么接受这样的解决方案,要么解释其局限性,以便获得更准确的目标解决方案。

如果你知道最大值,你可以这样做
有一个单独的数组,其长度为最大值

 int[max] secondarray;
    for(int i=o;i<arrayFirst.length;i++){
        if(secondarray[arrayFirst[i]]==0){
            secondarray[arrayFirst[i]]==arrayFirst[i];
         }else{
             result.add(arrayFirst[i]);
          }
     }

您可以在0 (n)内完成此操作,但是这将要求数组为整数。所需的空间大小可以为-2^32到2^32。你需要做的是找到原始数组的最大值和最小值。然后生成两个数组(arraynew+)和(arraynew-)

(arraynew+)的大小为max(arraorig)-min(arrayorig)如果arrayorig中的所有值都是+,则(arraynew+)的大小为max(arrayorig)。

如果所有值都为正,则size (arraynew-)将为零,否则它们将等于min(arrayorig)的绝对值。

然后你可以遍历数组,并在数组值对应的索引处增加1 (arraynew-)或(arraynew+)的值,如果值是正的,应该增加(arraynew+),否则如果它的负增量应该在(arraynew-)的索引处做(arraynew-),这等于数组的绝对值。那么所有值>1的(arraynew+)和((arraynew-)的索引都是arrayorig的不同值。

 void printRepeating(int arr[], int size)
 {
 int i;
   printf("The repeating elements are: n");
 for (i = 0; i < size; i++)
 {
 if (arr[abs(arr[i])] >= 0)
  arr[abs(arr[i])] = -arr[abs(arr[i])];
 else
  printf(" %d ", abs(arr[i]));
 }
  }

查找重复项就像排序一样困难。最好的办法是利用输入的某些属性来获得O(N)排序。