剑指Offer—位运算
位运算的基本操作
-
<< 左移
5<<2 左移两位, -
>> 右移 最高位是符号位
-
无符号右移( >>> ) n>>>=2;右移2位
Java没有无符号左移,左移就是删除最高位,再左移一位 -
& 按位与
-
| 按位或
-
^ 按位异或
-
异或的三种性质
- A^A=0;
- A^0=A;
- 如果A!=B,那么A和B至少有一位不同,A^B至少有一位为1;
位操作习题
eg1 J15 二进制中1的个数
编写一个函数,输入是一个无符号整数(以二进制串的形式),返回其二进制表达式中数字位数为 ‘1’ 的个数(也被称为 汉明重量).)。
public class Solution {
// you need to treat n as an unsigned value
public int hammingWeight(int n) {
int res=0;
while(n!=0){
res+=n&1;
n>>>=1;//记住这里是无符号右移
}
return res;
}
}
eg2 J65 不用加减乘除做加法
写一个函数,求两个整数之和,要求在函数体内不得使用 “+”、“-”、“*”、“/” 四则运算符号。
思路
两种情况:
- 不进位的加法
sum=a^b; - 进位的加法
carry=a&b; //这里需要左移
单独把 sum和carry再相加
class Solution {
public int add(int a, int b) {
/*
两种情况:
不进位的加法
sum=a^b;
进位的加法
carry=a&b;
单独把 sum和carry再相加
*/
while(b!=0){
int sum=a^b;
int carry=(a&b)<<1;//进位的需要左移
a=sum;
b=carry;//直到进位全部处理完成
}
return a;
}
}
eg3 J56-I 数组中数字出现的次数
一个整型数组 nums 里除两个数字之外,其他数字都出现了两次。请写程序找出这两个只出现一次的数字。要求时间复杂度是O(n),空间复杂度是O(1)。
思路:分组异或
如何进行分组?
- 因为必有两个数不同,所以利用他们不同的那个位置(0和1)
整体思路-三步
- to the the results of k=a^b
- to get the mask
- to group the nums[]
class Solution {
public int[] singleNumbers(int[] nums) {
/*
思路:分组异或
异或的性质:
1.A^A=0
2.A^0=A
3.如果A!=B,那么A和B至少有一位不同,A^B至少有一位为1;
因为必有a!=b
所以a^b 至少有一位不等,我们按照 那个位置来区分两组数据
*/
//to get the result of k=a^b;
int k=0;
for(int i=0;i<nums.length;i++){
k=k^nums[i];
}
//k = a^b
//如果找到 位 为1
//to get the mask 0001000
int mask=1;
//这里只能写 ==0;!=1可能会超时
while((k&mask)==0){
mask<<=1;
}
//找到mask后,按照mask对nums分组 mask:0000a00000
//to group nums
int x=0;
int y=0;
for(int num:nums){
if((num&mask)==0){
x^=num;
}
else{
y^=num;
}
}
return new int[] {x,y};
}
}