题目描述:
一个整型数组 nums 里除两个数字之外,其他数字都出现了两次。请写程序找出这两个只出现一次的数字。要求时间复杂度是O(n),空间复杂度是O(1)。
示例 1:
输入:nums = [4,1,4,6]
输出:[1,6] 或 [6,1]
题目分析:
我们可以将所有的元素进行 ^ 操作,结果为出现了1次的2个元素相 ^ 的结果res,那么怎么分别得到这2个数字呢?
我们可以设置临时变量t = 1;让 t 和 res进行 & ,如果t & res == 0,t<<1;否则直到t & res == 1,即找到了res中由低位到高位第1个1时停止。
因为所有的元素中只有2个元素不同,那么异或后的res肯定不为0,那么说明res中至少有1位为1,这说明2个只出现1次的元素的某一对应位置的元素一个数字0,一个为1,所以,我们可以将为0的分为一组进行,为1的分为一组进行异或,其他元素都出现2次,所以被分到同一个组,^的结果为0,所以每组最后的结果都是出现1次的那个数。
简要概括:
3步走策略:
(1)将所有的数字进行异或,结果为res;
(2)根据res寻找不同的两个数中哪一位为1。 将t(初始值为1)&res,若等于0,t<<1,重复上述工作;否则停止;
(3)根据(2)中为1的那一位进行分组,这样不同的2个数就被分在不同的组,分组中的元素进行^操作,结果就是所求元素。
参考代码:
public int[] singleNumbers(int[] nums) {
if(nums == null || nums.length == 0)return new int[]{};
int res = 0;
for(Integer num : nums){
res ^= num;
}
int t = 1;
while((res & t) == 0){
t <<= 1;
}
int ans1 = 0;
int ans2 = 0;
for(int i = 0; i < nums.length; i++){
if((nums[i] & t) == 0)ans1 ^= nums[i];
else ans2 ^= nums[i];
}
return new int[]{ans1, ans2};
}
注意:
int res = 0;//这里必须注意
for(Integer num : nums){
res ^= num;
}
res初始值为0,因为0异或任何数都等于原数