&:对应为有0,则为0,其余为1;
n & (n - 1 ):去掉右边第一个1。
n & 1 == 1 则说明概述最右位位1,即n为奇数。
|:对应位有1,则是1,其余为0。
^:一样的位置为0,不一样的位置为1。
n ^ n = 0;
0 ^ n = n;
按位异或^可以用于10以内的加法(因为不会产生进位),如果产生进位,则需要用到按位与&配合,假如a & b = c,我们知道,只有相同位置都是1才为1,二进制加法为两个1则进位。所以c中有1的位都是要进位(左移一位)的。可以用&记录需要进位的数。
所以加减法可以用&和^的配合来完成。看题
题目描述:
代码实现如下:
//方法一:循环
class Solution {
public int add(int a, int b) {
while (b != 0) {
int c = (a & b) << 1;
a ^= b;
b = c;
}
return a;
}
}
//方法二:递归
class Solution {
public int add(int a, int b) {
if (b == 0) return a;
int c = (a & b) << 1;
a ^= b;
return add(a,c);
}
}
再来看这题,求二进制中1的个数。上面说到,n&(n - 1)可以去掉右边第一个1.可以利用这个特性求解本题。
题目描述:
代码实现如下:
public class Solution {
public int hammingWeight(int n) {
int count = 0;
while (n != 0) {
n = n & (n - 1);
count++;
}
return count;
}
}
下面再来看这题,求数组中数字出现的次数I。
题目描述:
解题思路:
全员进行异或操作即可。考虑异或操作的性质:对于两个操作数的每一位,相同结果为 0,不同结果为 1。那么在计算过程中,成对出现的数字的所有位会两两抵消为 0,最终得到的结果就是那个出现了一次的数字。而本题中出现一次的数有两个。所以所有数异或的结果就是那两个只出现一次的数异或的结果。假定为这个结果为res。
那么这一方法如何扩展到找出两个出现一次的数字呢?
如果我们可以把所有数字分成两组,使得:
1、两个只出现一次的数字在不同的组中;
2、相同的数字会被分到相同的组中。
下面进行分组。首先考虑两个相同的数字一定要分在一组。这个很容易实现,我们只需要固定一个二进制位,只要两个数字在这一位相同,就肯定划分在一组。下面就是要将那两个只出现一次的数字x,y分到两个组中,两边分别异或和,相同的两两抵消,最后两边的异或和就是x,y。接下来就剩一个问题,如何把x,y分到两个组里面去。由于res是所有数字异或和,等同于x,y的异或和,x,y不相同,根据异或的性质,res二进制里面的1都是x,y不相同的地方,我们只要找到res中的任意一个1,这里解题我找的是右边第一位1,这个位置记为div,x,y在位置div上是1,根据异或性质,在这个位置上x,y不一样,一个为0,一个为1。然后在遍历数组依次将数组元素与div按位与&运算。那么x,y在&div是,肯定有一个为0,另一个非0。这样两个数就会被分到两个组中。然后分别求异或和就是答案。
代码实现如下:
class Solution {
public int[] singleNumbers(int[] nums) {
int res = 0;
for (int num : nums) {
res ^= num; //两个不同值的异或结果,假设为x,y
}
int div= 1;
while ((div & res) == 0) { //res里的所有1都是代表x,y不相同的位
div <<= 1; //找到res从右数的第一个1(x,y的不相等的位)
}
int a = 0,b = 0; //分组
for (int num : nums) {
if ((div & num) == 0) { //能把x,y分开,因为x,y得到的结果肯定是不同的。仔细理解,有点像负负得正。其次数组其他相同的数字也会被分到相同的组,因为相同的数字得到的结果相同。
a ^= num;
} else {
b ^= num;
}
}
return new int[]{a,b};
}
}
再来练习最后一道关于位运算的题。数组中数字出现次数II
题目描述:
这题比上一题其实简单一些,没有那么难理解。只要有思路就好做。首先题目说除了一个数字target外,其他的数字都出现三次,意味着没有target的情况下,各二进制位出现的次数都是3的倍数。统计所有数字的各二进制位中 1的出现次数,并对 3 求余,结果则为只出现一次的数字。
代码实现如下:
class Solution {
public int singleNumber(int[] nums) {
int[] counts = new int[32]; //统计个数
for (int i = 0;i < nums.length;i++) {
for (int j = 0;j < 32;j++) {
counts[j] += (nums[i] >> j & 1) == 1 ? 1 : 0; //将每个数字右移j位再&1,判断最后一位是不是1,然后加到对应位的counts中。
}
}
int target = 0;
for (int i = 31;i >= 0;i--) {
target <<= 1; //从后向前将结果输出,每个位置对应counts中的数字后左移判断下一个位置
if ((counts[i] % 3) == 1) { //说明target在该位置上是1
target |= 1;
}
}
return target;
}
}