题目:
Given an array of numbers nums
, in which exactly two elements appear only once and all the other elements appear exactly twice. Find the two elements that appear only once.
Example:
Input:[1,2,1,3,2,5]
Output:[3,5]
Note:
- The order of the result is not important. So in the above example,
[5, 3]
is also correct. - Your algorithm should run in linear runtime complexity. Could you implement it using only constant space complexity?
解题思路
- 首先根据异或性质,两个相同的数异或为0,0和任意数异或结果为任意数,数组中只有两个数出现1次,其余出现一次
- 根据以上性质,我们可以将数组所有值异或得到这两个数的异或结果temp = one ^ two
- 此时需要将这两个数分开,此时可以反向考虑,我们异或得到的结果肯定不为0那么这个temp的二进制表示某一位上必然有1的存在
- 我们可以根据某一位上1的存在根据这一位将原始数组分为两组,一组这一位上包含这个1,另一组这一位上不包含这个1,如果one和two这两个数在这一位都是1的话,根据异或性质这一位就是0,所以必然可以根据1这位将数组分为两部分
- 分组之后的数据再进行数组异或最后就可以得到one和two,因为分组之后一组之中只有一个数据出现1次,其余的数据全部出现两次,根据异或结果最后就是这个出现1次的数据
- 举例来说:示例中temp = 3 ^5 = 6,二进制表示是110,此时右数第二位表示为1,那么我们可以用010这个数据去和数据进行与操作,结果不为0的是一组结果为0的是另一组,就可以将3和5分别划分到两组中,然后组内数据相异或就可以找到对应的数字
源代码实现
public class SingleNumberIII {
public static void main(String[] args) {
/**
* Example:
*
* Input: [1,2,1,3,2,5] Output: [3,5]
*/
int[] nums = { 1, 2, 1, 3, 2, 5 };
int[] output = singleNumber(nums);
LeetCodeUtil.printIntArray(output, output.length);
}
public static int[] singleNumber(int[] nums) {
int[] result = new int[2];
int temp = 0;
// 假设这两个数变量分别是one和two,根据异或性质temp = one^two;
for (int i = 0; i < nums.length; i++)
temp ^= nums[i];
int count = 0;
// 找到第一个不为0的位,count用于计数还原
while ((temp & 1) == 0) {
temp >>= 1;
count++;
}
temp = 1;
// 此时根据count将这个数据还原,示例中还原为010形式,移位实现
while (count != 0) {
temp <<= 1;
count--;
}
// 根据temp进行分类实现结果
for (int i = 0; i < nums.length; i++) {
if ((temp & nums[i]) == 0)
result[0] ^= nums[i];
else
result[1] ^= nums[i];
}
return result;
}
}