给定一个带有n+2
元素的数组,数组中的所有元素都在1
到n
的范围内,并且所有元素只出现一次,除了两个出现两次的元素。
找到那两个重复的数字。例如,如果数组为[4, 2, 4, 5, 2, 3, 1]
,则n
为5,有n+2 = 7
个元素,除2和4外,所有元素只出现一次。
所以我的问题是如何使用XOR操作解决上述问题。我在其他网站上看到了解决方案,但我无法理解。请考虑以下示例:
arr[] = {2, 4, 7, 9, 2, 4}
xor = 2^4^7^9^2^4 = 14
(1110
)set_bit_no = xor & ~(xor-1) = (1110) & ~(1101) = 0010
。现在set_bit_no
只会将xor
设置为最右边的位。答案 0 :(得分:1)
是的,您可以使用XOR解决它。这个答案扩展到Paulo Almeida's great comment。
该算法的工作原理如下:
因为我们知道数组包含范围[1 .. n]中的每个元素,所以我们首先对数组中的每个元素进行异或,然后将结果与范围[1 .. n]中的每个元素进行异或。由于XOR属性,唯一元素被抵消,结果是重复元素的异或(因为重复元素总共被异或3次,而所有其他元素被异或两次并被取消)。这存储在xor_dups
。
接下来,在xor_dups
中找到一个1的位。再次,由于XOR的属性,在xor_dups
中设置为1的位意味着该位在二进制文件中是不同的表示重复的数字。可以选择任何一个1作为下一步,我的实现选择最不重要的。这存储在diff_bit
。
现在,将数组元素拆分为两组:一组包含从xor_dups
中选取的1位位置上具有0位的数字。另一组包含具有1位的数字。由于这个位在我们正在寻找的数字上有所不同,因此它们不能同时属于同一组。此外,每个号码的出现次数都归同一组。
所以现在我们差不多完成了。考虑具有0位的元素的组。将它们全部异或,然后将结果与范围[1..n]中在该位置上具有0位的所有元素进行异或,结果是该组的重复数字(因为仅有#s; s)在每组内重复一个数字,所有非重复数字都被取消,因为每一个都被异或两次,除了重复的数字被异常三次)。
冲洗,重复:对于具有1位的组,将它们全部异或,然后将结果与[1..n]范围内在该位置上具有1位的所有元素进行异或,并且结果是另一个重复的数字。
这是C:
中的一个实现#include <assert.h>
void find_two_repeating(int arr[], size_t arr_len, int *a, int *b) {
assert(arr_len > 3);
size_t n = arr_len-2;
int i;
int xor_dups = 0;
for (i = 0; i < arr_len; i++)
xor_dups ^= arr[i];
for (i = 1; i <= n; i++)
xor_dups ^= i;
int diff_bit = xor_dups & -xor_dups;
*a = 0;
*b = 0;
for (i = 0; i < arr_len; i++)
if (arr[i] & diff_bit)
*a ^= arr[i];
else
*b ^= arr[i];
for (i = 1; i <= n; i++)
if (i & diff_bit)
*a ^= i;
else
*b ^= i;
}
arr_len
是数组arr
的总长度(n+2
的值),重复的条目存储在*a
和*b
中(这些都是所谓的输出参数。)