在一个数组中查找重复元素的算法

10

我有一个任务,需要创建一个算法在包含数字值的数组中查找重复项。但是任务没有说明这些数字属于哪种类型,整数还是浮点数。我已经编写了以下伪代码:

 FindingDuplicateAlgorithm(A) // A is the array
      mergeSort(A);
      for  int i <- 0 to i<A.length
           if A[i] == A[i+1]
                 i++
               return  A[i]
           else
                 i++

我是否创建了一个高效的算法? 我认为我的算法存在问题,会多次返回重复的数字。例如,如果数组在两个索引中都包含2,则输出将包含...2, 2,...。我该如何更改它,以仅返回每个重复项一次? 我认为对于整数而言,这是一个好的算法,但对于浮点数呢?


2
注意不要使用A[i+1] -- 如果i = (A.length - 1),会发生糟糕的事情。你希望for循环只在i < A.length - 1时继续执行。 - Seth
7个回答

12

为了处理重复项,您可以执行以下操作:

if A[i] == A[i+1]:
    result.append(A[i]) # collect found duplicates in a list
    while A[i] == A[i+1]: # skip the entire range of duplicates 
        i++               # until a new value is found

+1 但是检测重复的浮点数并不比检测重复的整数更棘手。当且仅当 value1 == value2 时,两个浮点值才是相同的。 - Andreas Brinck
2
@Andreas:你说得对,但是对于浮点数,equalduplicate这两个词的意思是不同的。 - Björn Pollex
2
不,我不这么认为。如果且仅如果a == b,那么值a是另一个值b的重复,没有其他定义方式。 - Andreas Brinck
mergeSort(Arr); int i <- 0; for i<- Arr.lenght-1 if Arr[i] == Arr[i+1] return Arr[i] while A[i] = A[i+1] i++ - Elton.fd
@Space_C0wb0y 我会删除答案的第一段,因为它是不正确的。真正的情况是,大多数实数不能被准确地表示为IEEE 754浮点数。 - Andreas Brinck
显示剩余5条评论

10

你想在Java中查找重复项吗?

你可以使用HashSet。

HashSet h = new HashSet();
for(Object a:A){
   boolean b = h.add(a);
   boolean duplicate = !b;
   if(duplicate)
       // do something with a;
}

add()的返回值定义为:

如果集合中未包含指定元素,则返回true。

编辑: 我知道HashSet针对插入和包含操作进行了优化。但我不确定它是否足够快以满足您的要求。

编辑2: 我看到您最近添加了作业标签。如果这是一项作业,我不建议使用我的答案,因为它可能对算法课程来说过于“高级”。

http://download.oracle.com/javase/1.4.2/docs/api/java/util/HashSet.html#add%28java.lang.Object%29


2

您的答案看起来相当不错。首先进行排序,然后简单地检查相邻值可以得到O(n log(n))的复杂度,这非常高效。

归并排序是O(n log(n)),而检查相邻值只是O(n)

不过有一件事(正如其中一个评论中提到的),您的伪代码会导致堆栈溢出(哈哈)。内部循环应该是(在Java中):

for (int i = 0; i < array.length - 1; i++) {
    ...
}

如果您想显示哪些数字(和/或索引)是重复的,那么您需要将它们存储在一个单独的列表中。


1

我不确定您需要用什么语言编写算法,但是在我的问题的回答中有一些非常好的C++解决方案。这对您应该有所帮助。


1

O(n)算法:遍历数组并尝试将每个元素输入具有数字作为哈希键的哈希表/集合中。如果无法输入,则为重复项。


1
如果您没有新的内容,请不要回答此问题,因为这似乎与https://dev59.com/jG855IYBdhLWcg3wsWr3#4192865相同。如果您有新的内容,请扩展您的答案。 - Jeffrey Bosboom
在我的帖子中有两件不同的事情: 提到了复杂性以及必须从 .NET 的角度“尝试”插入值的事实。实际上,您链接中列出的代码会在 .NET CLR 中因为尝试插入已经存在的键而引发异常。在.NET中,在插入之前必须使用 trygetvalue()。 - Maksood

0

你的算法存在缓冲区溢出。 i 从0开始,因此我假设数组 A 的索引是基于零的,即第一个元素是 A [0],最后一个元素是 A [A.length-1] 。现在,i 计数到 A.length-1,并且在循环体中访问 A[i+1],这对于最后一次迭代来说超出了数组范围。或者简单地说:如果您要将每个元素与下一个元素进行比较,则只能进行长度-1次比较。

如果您只想报告重复项一次,我会使用一个布尔变量 firstDuplicate,当您发现重复项时将其设置为false,并在数字与下一个数字不同的情况下将其设置为true。然后,仅在 firstDuplicate 为true时报告第一个重复项,即仅在报告重复数字时报告它们。


0
 public void printDuplicates(int[] inputArray) {
    if (inputArray == null) {
        throw new IllegalArgumentException("Input array can not be null");
    }
    int length = inputArray.length;

    if (length == 1) {
        System.out.print(inputArray[0] + " ");
        return;
    }

    for (int i = 0; i < length; i++) {

        if (inputArray[Math.abs(inputArray[i])] >= 0) {
            inputArray[Math.abs(inputArray[i])] = -inputArray[Math.abs(inputArray[i])];
        } else {
            System.out.print(Math.abs(inputArray[i]) + " ");
        }
    }
}

1
请解释你的答案。SO 的存在是为了教育人们,而不仅仅是回答问题。 - Machavity
确保。这里的主要思想是使用数组中的数字作为索引。步骤1-在循环中更改所有小于index inputArray [i]的数字的符号。步骤0-检查数字是否为负数。如果是,则表示有另一个数字指向当前元素并已更改它。 - smaiakov
2
@smaiakov,如果数组元素本身比数组大小还大怎么办?我们将会得到越界异常。 - Kiran

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接