与运算:有0则0。
或运算:有1则1。
与或运算:相同为0,相异为1。
题目描述:
给定一个整数数组 nums,其中恰好有两个元素只出现一次,其余所有元素均出现两次。 找出只出现一次的那两个元素。
样例:
输入: [1,2,1,3,2,5]
输出: [3,5]
分析:
异或运算的性质:任何一个数字异或它自己都等于0。
首先考虑如果数组中只有一个元素出现一次,其余元素均出现了两次。我们从头到尾依次异或数组中的每一个数字,那么最终的结果刚好是那个只出现一次的数字。
//执行用时 : 1 ms, 在Single Number的Java提交中击败了99.59% 的用户
//内存消耗 : 40.2 MB, 在Single Number的Java提交中击败了80.05% 的用户
public int singleNumber(int[] nums) {
if(nums==null||nums.length==0)
return 0;
int re=nums[0];
for(int i=1;i<nums.length;i++) {
re=re^nums[i];
}
return re;
}
回到这个题目中,数组中有两个元素只出现了一次,其余均出现了两次。如果我们能够根据某种规则将原数组拆分成两个子数组,使得每个子数组中只包含一个元素出现了一次其余均出现了两次,最终就可以找到唯一两个在数组中只出现一次的数据。
划分的规则如下:
- 从头到尾依次异或数组中的每一个数字,最终得到的是唯一两个在数组中只出现一次的数据异或之后的结果。
- 因为这两个数字肯定不同所以得到的结果中在二进制表示中至少有一位是1,我们在结果中找到第一个为1的位置,记为n,根据第n位是否为1将数组分成两部分。
- 每一个子数组都包含一个只出现一次的数字,而其他数字均出现了两次。
如何判断一个数字的二进制表示中第i位是不是1?
将该数字和1做与运算,如果结果为0,则证明该位不为1,将1左移继续判断下一位;否则该位为1。
public int[] singleNumber(int[] nums) {
int []re=new int[2];
if(nums==null||nums.length<2)
return re;
int result=nums[0];
for(int i=1;i<nums.length;i++)
result^=nums[i];
int index=getFirstBit1(result);//第一个二进制为1的位置(从最左侧开始)
re[0]=0;re[1]=0;
for(int i=0;i<nums.length;i++) {
if(isBit1(nums[i],index))
re[0]^=nums[i];
else
re[1]^=nums[i];
}
return re;
}
private boolean isBit1(int i, int index) {
// TODO Auto-generated method stub
int num=1<<index;
if((i&num)!=0)
return true;
return false;
}
private int getFirstBit1(int result) {
int i=1,index=0;
while(i!=0) {
if((result&i)==0) {
index++;
i=i<<1;
}else {
break;
}
}
return index;
}