位运算
对于编程语言,位运算更加接近计算机底层,在可以使用位运算的时候使用位运算,可以提升程序的运行效率。
1. 基础知识
Java 中有如下位运算符(Ref):
- 位与:
&
- 位或:
|
(inclusive OR) - 位异或(XOR):
^
(exclusive OR) - 位取反:
~
- 带符号移位,符号位不变,高位(低位)补零:
>>
(右移);<<
(左移); - 无符号移位,高位补零,符号位也移动(只有无符号右移,没有无符号左移!!):
>>>
(右移);
一些例子:
1 | int a = 5, b = 7; |
2. 例子
2.1. 二进制中1的个数
请实现一个函数,输入一个整数(以二进制串形式),输出该数二进制表示中 1 的个数。例如,把 9 表示成二进制是 1001,有 2 位是 1。因此,如果输入 9,则该函数输出 2。
该题欲找出二进制串中1的个数,可以使用按位右移的方法,逐个的将 1
移动到最低位,直到当前数字变为零为止。
1 | public class Solution { |
参考题解,这一题还有更优化的解法。
因为 n & (n - 1)
的效果是消去 n
的最低位的1
, 例如 n = 12, 对应2进制为 0000,1100
(以8位为例子), n - 1 的二进制表达为:0000,1011
, 可以看到,效果是将原数中最低位的 1 变为 0, 然后更低位全部变为 1。故 n & (n - 1)
的效果是将n 中最低位的 1 变为 0,其余位不变。
根据上述分析,有如下实现:
1 | public class Solution { |
2.2 数值的整数次方
实现函数double Power(double base, int exponent),求base的exponent次方。不得使用库函数,同时不需要考虑大数问题。
对于这道题来说,位移运算是一个小的技巧补充 - 用于实现快速的除2 计算。
这道题的核心难点其实比较像前面总结过的大数越界问题中,使用快速求幂法 的那道题目。具体的,是通过将指数不断的除2,来快速求解答案。求解过程中,需要分奇偶讨论,设指数为 n,
- 当 n 为偶数时,$x^n = (x^2)^{n/2}$;
- 当 n 为奇数时,$x^n = (x^2)^{n//2}*x$ ($n//2$ 表示向下取整)。
可以看到,最终的答案分为两部分,一个是 x 的乘方部分,一个是额外的 乘子 - 当奇数时会产生。
根据上述分析,得到如下代码:
1 | class Solution { |
需要注意的是,这里将指数先放置到了 long 型的变量当中,主要是因为 int 的范围为 $2^{-31} 到 2^{31}-1$,所以,当 n 为 $2^{-31}$ (-2147483648) 的时候,n = -n;
会出现溢出问题。所以这里先将 n 放置到了一个 long 型的整数中,从而避免了该问题。
根据题解,上述的代码可以进一步精简,如下:
1 | class Solution { |