剑指offer(八):位运算
题目一:二进制中 1 的个数
方法一:逐位进行操作
public class Solution {
public int hammingWeight(int n) {
int res = 0;
while(n != 0) {
res += n & 1;
//按位右移补零操作符。左操作数的值按右操作数指定的位数右移,移动得到的空位以零填充。
n >>>= 1;
}
return res;
}
}
方法二:n&(n-1)–精巧
public class Solution {
public int hammingWeight(int n) {
int res = 0;
while(n != 0) {
res++;
n &= n - 1;
}
return res;
}
}
题目二:数组中数字出现的次数
异或运重要性质:两个相同数字异或为 0 ,即对于任意整数 a⊕a=0 。因此,若将nums 中所有数字执行异或运算,留下的结果则为出现一次的数字 x。
public int[] singleNumber(int[] nums) {
int x = 0;
for(int num : nums) // 1. 遍历 nums 执行异或运算
x ^= num;
return x; // 2. 返回出现一次的数字 x
}
题目解法:
设两个只出现一次的数字为 x, y,由于 x/y ,则 x和 y二进制至少有一位不同(即分别为 0和 1 )根据此位可以将 nums 拆分为分别包含 x和 y的两个子数组。
class Solution {
public int[] singleNumbers(int[] nums) {
int x = 0, y = 0, n = 0, m = 1;
for(int num : nums) // 1. 遍历异或
n ^= num;
//找出两个只出现一次的数的数位上不相等的位,根据这个将两个只出现一次的数分开
while((n & m) == 0) // 2. 循环左移,计算 m
m <<= 1;
for(int num: nums) { // 3. 遍历 nums 分组
if((num & m) != 0) x ^= num; // 4. 当 num & m != 0
else y ^= num; // 4. 当 num & m == 0
}
return new int[] {x, y}; // 5. 返回出现一次的数字
}
}
题目三:数组中数字出现的次数 II
**思想:**统计所有数字的各二进制位中 1的出现次数,并对3求余,结果则为只出现一次的数字
class Solution {
public int singleNumber(int[] nums) {
int[] counts = new int[32];
for(int num : nums) {
for(int i = 0; i < 32; i++) {
counts[i] += num & 1; // 更新第 i 位 1 的个数之和
num >>= 1; // 第 i 位 --> 第 i 位
}
}
int res = 0, m = 3;
for(int i = 31; i >= 0; i--) {
res <<= 1;
res |= counts[i] % m; // 恢复第 i 位
}
return res;
}
}
题目四:不用加减乘除做加法
设两数字的二进制形式 a, b,求其和 s = a + b,a(i)代表 a 的二进制第 i位,则分为以下四种情况:
无进位和 与 异或运算 规律相同,进位 和 与运算 规律相同(并需左移一位)
class Solution {
public int add(int a, int b) {
//当进位为 0 时跳出
while(b != 0) {
int c = (a & b) << 1; // c = 进位
a ^= b; //非进位和
b = c; //进位
}
return a;
}
}