C 查找数组中的重复元素

C 查找数组中的重复元素,c,arrays,algorithm,complexity-theory,big-o,C,Arrays,Algorithm,Complexity Theory,Big O,有一个大小为n的数组,数组中包含的元素介于1和n-1之间,因此每个元素出现一次,只有一个元素出现多次。我们需要找到这个元素 虽然这是一个非常常见的问题,但我仍然没有找到正确的答案。大多数建议是,我应该将数组中的所有元素相加,然后从中减去所有索引的总和,但如果元素的数量非常大,这将不起作用。它会溢出来的。还有一些关于使用异或门的建议,我不清楚 我提出了这个算法,它是加法算法的一个增强,将在很大程度上减少溢出的机会 for i=0 to n-1 begin : diff = A[i] -

有一个大小为n的数组,数组中包含的元素介于1和n-1之间,因此每个元素出现一次,只有一个元素出现多次。我们需要找到这个元素

虽然这是一个非常常见的问题,但我仍然没有找到正确的答案。大多数建议是,我应该将数组中的所有元素相加,然后从中减去所有索引的总和,但如果元素的数量非常大,这将不起作用。它会溢出来的。还有一些关于使用异或门的建议,我不清楚

我提出了这个算法,它是加法算法的一个增强,将在很大程度上减少溢出的机会

for i=0 to n-1
  begin :
    diff = A[i] - i;
    sum  = sum + diff;
  end

diff
包含重复元素,但使用此方法无法找到重复元素的索引。为此,我需要再次遍历数组,这是不可取的。有谁能想出一个不涉及加法或XOR方法的更好的解决方案吗?

根据问题描述的约束条件,您可以用很多方法来思考这个问题

如果您知道只有一个元素被复制了,那么有很多方法可以解决这个问题。一个特别聪明的解决方案是使用按位异或运算符。XOR具有以下有趣的特性:

  • XOR是关联的,所以(x^y)^z=x^(y^z)
  • XOR是可交换的:x^y=y^x
  • XOR是它自己的逆:当x=y时,x^y=0
  • XOR具有零作为标识:x^0=x
  • 这里的属性(1)和(2)意味着,当取一组值的XOR时,对元素应用XOR的顺序无关紧要。可以根据需要对图元重新排序或对其分组。属性(3)表示如果对同一个值进行多次异或运算,则返回零;属性(4)表示如果将任何值与0进行异或运算,则返回原始数字。把所有这些属性放在一起,你会得到一个有趣的结果:如果你对一组数字进行XOR运算,结果就是该组中出现奇数次的所有数字的XOR运算。这样做的原因是,当您将出现偶数次的数字XOR在一起时,您可以将这些数字的XOR分解为一组对。每对异或通过(3)到0,所有这些零的组合异或通过(4)返回零。因此,偶数重数的所有数字都被抵消了

    要使用此解决原始问题,请执行以下操作。首先,将列表中的所有数字进行异或运算。这就给出了奇数次出现的所有数字的异或,结果是除了重复的数字之外,从1到(n-1)的所有数字。现在,用从1到(n-1)的所有数字的XOR对这个值进行XOR。然后,这将使1到(n-1)范围内以前未取消的所有数字取消,只留下重复的值。此外,这在O(n)时间内运行,并且只使用O(1)空间,因为所有值的XOR都适合于单个整数

    在你最初的帖子中,你考虑了另一种方法,即使用从1到n-1的整数之和为n(n-1)/2这一事实。但是,您担心这会导致整数溢出并导致问题。在大多数机器上,这会导致溢出是正确的,但(在大多数机器上)这不是问题,因为算术是使用固定精度整数完成的,通常是32位整数。当发生整数溢出时,生成的数字并非毫无意义。相反,它只是计算实际结果时得到的值,然后去掉除最低32位以外的所有内容。从数学上讲,这就是所谓的模运算,计算机中的运算是模232运算。但是,更一般地说,对于某些固定的k,整数是以k为模存储的

    幸运的是,许多你从普通算术中了解和喜爱的算术定律仍然存在于模运算中。我们只需要更精确地使用术语。我们说x与y模k(表示为x)全等≡k y)如果x和y除以k时留下相同的余数。在物理机器上工作时,这一点很重要,因为在大多数硬件上发生整数溢出时,结果值与模k的真值一致,其中k取决于字的大小。幸运的是,以下定律在模运算中适用:

    例如:

  • 如果x≡k y和w≡kz,然后x+w≡ky+z
  • 如果x≡k y和w≡kz,然后xw≡克孜
  • 这意味着,如果您希望通过查找数组元素的总和并减去期望的总和来计算重复值,那么即使存在整数溢出,所有操作都会正常进行,因为标准算术仍然会在硬件中生成相同的值(模k)。也就是说,你也可以使用基于XOR的方法,它根本不需要考虑溢出。p> 如果不能保证只有一个元素被复制,但可以修改元素数组,那么有一个漂亮的算法可以找到复制的值。描述如何完成此操作。直观地说,您可以尝试使用数组对序列进行排序,其中元素数组本身被循环使用,以容纳存储桶的空间

    如果不能保证只复制了一个元素,并且无法修改元素数组,那么问题就更难了。这是一个经典的(也是很难的!)面试问题,据说Don Knuth花了24小时才解决。诀窍是通过将数组视为从数字1-n到1-(n-1)的函数,将问题简化为的实例