1、给定一个 int 类型数值 num,求其二进制表示中 1 的个数:
分析:
以 num = 219 为例:
num = 1 1 0 1 1 0 1 1
num - 1 = 1 1 0 1 1 0 1 0
num & (num -1) = 1 1 0 1 1 0 1 0
则每次 num & (num -1 ) 就可以 将其 二进制最后面的 1 去掉(变为 0 ),同时 用 一个 count 计数 ,循环以上处理,直至 num 变为0 即可,代码如下:
int countBit(int num) { int count = 0; while (num != 0) { num = num & (num - 1); count++; } return count; } public static void main(String[] args) { BitCount bitCount = new BitCount(); System.out.println(bitCount.countBit(0b11011011)); // 结果为 :6 System.out.println(bitCount.countBit(0xFFFFFFFF)); // 结果为 :32 }
2、两数相除 (LeetCode 原题 https://leetcode-cn.com/problems/divide-two-integers/)
分析:由于不能使用乘法、除法、mod运算符号,就只能用到 加法、减法、位移来解决;示例 dividend = 191、 divisor = 5 转为 二进制计算,如下:
dividend = 0b10111111 ==> 191
divisor = 0b101 ==> 5
dividend / divisor = 0b100110 ( ==> 38) 余数 为 0b1 ( ==> 1)
因此,我们可以用 位移 + 减法来做运算,思路如下:
二进制 每向左移动1位,相当于 乘于 2 ,向右移动1位 相当于除以 2 ,过程如下:
移位:dividend 大于divisor ,可以使用 divisor 左移;divisor = 5 向左移动 5 位(注意:只能是5位,如果再多左移1 位,就超过 dividend 了 ) ,得到新值:a = 0b10100000 ; ;
减法:dividend = dividend - a = 0b11111 , 大于 divisor,可以继续 位移 ;
移位 :101 左移2位 a = 0b10100
减法: dividend = dividend - a = 0b11111 - 0b10100 = 0b1011 ,大于 0b101,还可以继续位移;
位移 : 0b101左移1位 a = 0b1010;
减法 :dividend = dividend - a = 0b1011 - 0b1010= 0b1 ,小于 0b101,则结束位移 ;
将以上6步 1 位移的次数 累加起来:
result = (第1步 的 1 << 5 )+ (第3步的 1<<2 ) + (第5步的 1<<1 )
= 0b100110
以上只考虑了正整数的情况,负数 可以先转换为 正整数 ,得到的商 再加一个符号即可;
考虑 int 的取值范围 ,当 dividend 很大 , divisor 很小的时候,容易溢出;
源码如下:
public int divide(int dividend, int divisor) { // 0 不能作为除数 if (divisor == 0) { throw new IllegalArgumentException("参数异常"); } // 0 除以 任何数 都是 0 if (dividend == 0) { return 0; } // 任何数 除以 1 ,等于任何数 if (divisor == 1) { return dividend; } // 自己除以自己 得 1 if (dividend == divisor) { return 1; } // 除数 -1 时 , 最小 MIN_VALUE 变成 正整数时,会超出 Integer 的表示范围 if (divisor == -1) { if (dividend == Integer.MIN_VALUE) { return Integer.MAX_VALUE; } } // 判断 2个数的符号是不相同 int sign = (dividend >> 31) ^ (divisor >> 31); if (sign == 0) { // 2个数 符号相同 sign = 1; } else { // 2个数 符号不同 sign = -1; } // 全部转换为 正整数计算 // 考虑 Integer.MIN_VALUE 变为 正整数时候,会越界,所以 转换为 long 类型计算 long a = (dividend > 0) ? (long) dividend : 0L - (long) dividend; long b = (divisor > 0) ? (long) divisor : 0L - (long) divisor; // 计算 a / b if (a == b) { return sign; } if (a < b) { return 0; } // 最终返回的结果 long res = 0; // 最大位移(左移动)的位数 int count = 0; while (a >= b) { b <<= 1; count++; } if (count == 0) { return 0; } while (count > 0 ) { count--; b >>= 1; if (a >= b) { res += 1 << count; a = a - b; } } if (sign == -1) { res = 0 - res; } return res >= Integer.MIN_VALUE && res <= Integer.MAX_VALUE ? (int) res : Integer.MAX_VALUE; }