1 位移的妙用
1.1 位1的个数
问题描述
编写一个函数,输入是一个无符号整数(以二进制串的形式),返回其二进制表达式中数字位数为 ‘1’ 的个数(也被称为汉明重量)。详见leetcode191
问题分析
可以将输入的无符号整数各个bit位依次与1进行与运算,通过设置计数器,与运算的结果为1,计数器加1,上面提到的依次可以通过移位运算来实现,具体是指无符号整数右移或者1左移
代码实现
1左移方式
public int hammingWeight ( int n) {
int count = 0 ;
for ( int i = 0 ; i< 32 ; i++ ) {
if ( ( n& ( 1 << i) ) != 0 ) {
count++ ;
}
}
return count;
}
2 无符号整数右移方式
public int hammingWeight ( int n) {
int count = 0 ;
for ( int i = 0 ; i< 32 ; i++ ) {
if ( ( ( n>> i) & 1 ) != 0 ) {
count++ ;
}
}
return count;
}
优化思路
任意无符号整数n与n-1相差1,表现在bit位上相差最低位1,即n&n-1的结果与n相差1,可以通过不断的执行n&n-1,直至n=0 来统计n中1的个数
代码实现
ublic int hammingWeight ( int n) {
int count = 0 ;
while ( n!= 0 ) {
n = n& ( n- 1 ) ;
count++ ;
}
return count;
}
1.2 比特位计数
问题描述
给你一个整数 n ,对于 0 <= i <= n 中的每个 i ,计算其二进制表示中 1 的个数 ,返回一个长度为 n + 1 的数组 ans 作为答案。
问题分析
可以看到,这仍然是统计二进制中1的个数问题,相比于上一题,只是增加了需要统计的元素数量,所以只需要加一层循环即可,无论是使用基本方法还是优化方法,都可以实现,这里提供优化方法的代码实现,大家可以自主实现基本方式
代码实现
public int [ ] countBits ( int n) {
int [ ] ans = new int [ n+ 1 ] ;
for ( int i = 0 ; i<= n; i++ ) {
int count = 0 ;
int x = i;
while ( x!= 0 ) {
x = x & ( x- 1 ) ;
count++ ;
}
ans[ i] = count;
}
return ans;
}
1.3 颠倒无符号整数
问题描述
颠倒给定的 32 位无符号整数的二进制位。详见leetcode190
问题分析
对于32位无符号整数,调到之后变成31-i位,可以将无符号整数通过不断移位与1进行与运算,获取当前比特位的值,再通过移位操作放到指定的位置。
代码实现
public int reverseBits ( int n) {
int power = 31 ;
int reversed = 0 ;
while ( power>= 0 ) {
reversed += ( n& 1 ) << power;
n = n >>> 1 ;
power-- ;
}
return reversed;
}
2 通过位运算实现四则运算
2.1 通过位运算实现加法
问题描述
给你两个整数 a 和 b ,不使用 运算符 + 和 - ,计算并返回两整数之和。详见leetcode371
问题分析
不使用运算符 + 和 - ,可以考虑通过位运算来实现加法。两个比特位相加时,相同为0,不同为1,进位可以通过两个比特位进行与运算判断,因为只有两个比特位都为1时才会产生进位,此时啊a&b = 1,进位的值是(a&b)<<1;
代码实现
public int getSum ( int a, int b) {
while ( b!= 0 ) {
int flag = ( a& b) << 1 ;
a = a^ b;
b = flag;
}
return a;
}
2.2 通过位运算实现乘法
问题描述
递归乘法。 写一个递归函数,不使用 * 运算符, 实现两个正整数的相乘。可以使用加号、减号、位移,但要吝啬一些。详见leetcode08.05
问题分析
不使用*运算符,可以考虑累加,但题目中说要吝啬使用,可以考虑移位运算。可以以两个加数中较大的作为基数,通过判断较小值对应的比特位是否为1,来判断基数是否需要累加,这样可以通过较少的加法和移位运算来实现乘法。
代码实现
public int multiply ( int A , int B ) {
int max = Math . max ( A , B ) ;
int min = Math . min ( A , B ) ;
int result = 0 ;
while ( min!= 0 ) {
if ( ( min& 1 ) != 0 ) {
result += max;
}
min = min >> 1 ;
max += max;
}
return result;
}
总结
位运算的题目往往比较简单,可以通过移位来实现我们之前所谓的循环遍历,可以通过与、或、非、异或等基本运算实现对指定比特位的获取和设置,获取和设置技巧详见位运算规则 。再结合题目要求,进行操作即可。