这三道题目很考察bit操作思路。使用了下列几个技巧。

1.如何取得一个int数字二进制表示的每一位,方法就是使用逻辑右移操作,逐位与1做&操作;

2.亦或xor是一个很关键的操作,0与任意一个数做偶数次xor结果是0,奇数次结果是1。

3.位操作是满足交换律的,因为每一个bit是满足的。

4.如何得到一个int的最右侧的第一个1?方法是与其相反数做&。因为相反数是取反加一,取反以后,第一个1右侧的0变为1,加一以后这些1变为0,相应的第一个1变回为1,比这个1高的位全是0,因为取反。最终就得到了一个只包含最右侧1的掩码,其余是0。这个方法是O1的。


第一题:​​https://leetcode.com/problems/single-number/?tab=Description​

第一道题就是用了2,3性质直接得到的。使用交换律把相同的连续排放,最后xor结果是0,再和只出现一次的xor,得到本身。代码不贴了,很简单。


第二题:​​https://leetcode.com/problems/single-number-ii/?tab=Description​

第二题是说有些数字出现了3次,有一个只出现一次。

其实是之前题目的延伸,可以使用位运算得到所有这列题目的规律,leetcode里面有。不过这里我觉得另一个思路很好很直观,也可以给出通解,只不过是O(32n)的。思路就是逐位相加,和取模,比如其余出现了k次,就模k,那么余数就是单独的那个数字在该位的二进制。

public int singleNumber(int[] nums) {
int r = 0 ;
for(int i = 0; i < 32; i++){
int sum = 0;
for(int j : nums){
sum += (j >> i) & 1;
}
sum %= 3;
r |= (sum << i);
}
return r;
}




第三题:​​https://leetcode.com/problems/single-number-iii/?tab=Description​

是第一题的变种,一些出现两次,有两个数字a,b不同且各出现一次。先做一趟xor假设结果为x,那么x是a,b亦或的结果,因为二者不同那么x中必然有一位是1,我们使用方法4得到这个1,是一个只包含一位1的掩码。然后做第二趟xor,这次使用掩码分组操作。就可以区分a和b。其余额元素也会分组,不过不影响结果,因为肯定是在某一组中出现两次。

public int[] singleNumber(int[] nums) {
int[] r = new int[2];
int xor = 0;
for(int i : nums){
xor ^= i;
}
xor &= (-xor);
for(int i : nums){
if((i & xor) == 0)
r[0] ^= i;
else
r[1] ^= i;
}
return r;
}