LeetCode 191
https://leetcode.cn/problems/number-of-1-bits/
方法1:
思路分析
32位二进制表示的整数,每一位进行判断,同时进行统计
代码实现
class Solution:
def hammingWeight(self, n: int) -> int:
count = 0
for i in range(32):
if n & (1<<i) != 0:
count += 1
return count
方法2:
思路分析;
运用性质,将n的二进制表示的最后一个1变成0:n&(n-1)
每次执行 n=n&(n-1),n就会少一个1,可重复循环执行,直到n==0,循环次数即为1的个数
代码实现
class Solution:
def hammingWeight(self, n: int) -> int:
count = 0
while n!= 0:
n = n & (n-1)
count += 1
return count
总结:
方法1循环次数取决于原始数字的位数,方法2循环次数取决于1的个数,方法2比方法1效率更高
LeetCode 338. 比特位计数
https://leetcode.cn/problems/counting-bits/description/
思路分析:
参考上述 位1的个数
代码实现
class Solution:
def countBits(self, n: int) -> List[int]:
# 方法1:硬解
bits = [0]*(n+1)
for i in range(n+1):
for j in range(32):
bits[i] += (i & (1<<j) != 0)
return bits
class Solution:
def countBits(self, n: int) -> List[int]:
# 方法2:比特位计数
bits = [0]*(n+1)
for i in range(n+1):
k = i
while i!=0:
i=i&(i-1)
bits[k] += 1
return bits
LeetCode 190. 颠倒二进制位
https://leetcode.cn/problems/reverse-bits/
思路分析
方法1:
n的二进制表示的从低到高第i位,在颠倒之后变成了第 31-i (0≤i<32)
从低到高遍历n的二进制表示的每一位,将其放在其在颠倒之后的位置,最后相加即可
举例:看个16位的演示下
原始数据 1001 1111 0000 0110 (低位)
第一步:获得n的最低位0,将其右移16-1=15位,得到
reversed:0*** **** **** ****
n右移1位:0100 1111 1000 0011
第二步:继续获得n的最低位1,将其右移15-1=14位,并与reversed相加得到:
reversed:01** **** **** ****
n右移1位:0010 0111 1100 0001
继续,一直到n全部变成0
方法2:分块思想
n的二进制有32位,将n的二进制表示分成较小的块,然后将每个块的二进制位分别颠倒,最后将每个块的结果合并得到最终结果
代码实现
方法1
class Solution:
def reverseBits(self, n: int) -> int:
result = 0
power = 31
while n:
result += ((n&1)<<power)
power-=1
n>>=1
return result
注:python不用考虑溢出问题
if __name__ == '__main__':
n = 28 # 1 1100
print(type(n), n) # 28 1 1100
print(type(n << 31), (n << 31)) # 60129542144 1110 0000 0000 0000 0000 0000 0000 0000 0000 (36位)
print(type(n << 63), (n << 63)) # 258254417031933722624
# 1110 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 (68位)
方法2:
public class Solution {
// you need treat n as an unsigned value
public int reverseBits(int n) {
n = (n>>>16) | (n<<16);
n = ((n & 0xff00ff00) >>> 8) | ((n & 0x00ff00ff) << 8);
n = ((n & 0xf0f0f0f0) >>> 4) | ((n & 0x0f0f0f0f) << 4);
n = ((n & 0xcccccccc) >>> 2) | ((n & 0x33333333) << 2);
n = ((n & 0xaaaaaaaa) >>> 1) | ((n & 0x55555555) << 1);
return n;
}
}
计算机中,位运算的效率比加减乘除效率更高,在高性能软件的源码中大量应用,计算机里各种运算本质上都是位运算
LeetCode371. 两整数之和
https://leetcode.cn/problems/sum-of-two-integers/
思路分析
两个二进制位相加的情况
[1] 0 + 0 = 0
[2] 0 + 1 = 1
[3] 1 + 0 = 1
[4] 1 + 1 = 0 (发生了进位,实际10)
两个位加的时候,需要考虑两个问题:
进位部分:
都是1的情况才进位 => a&b
然后位数由1位变成两位 => 左移一位,(a&b)<<1
不进位部分:
相同为0,不相同为1 => a^b
结论
于是,我们可以将整数 a 和 b 的和,拆分为a和b的无进位加法结果与进位结果的和
代码实现
java实现
class Solution {
public int getSum(int a, int b) {
while(b != 0){
int sign = (a&b)<<1;
a = a^b;
b = sign;
}
return a;
}
}
python实现
python中没有溢出,需要特殊处理
class Solution:
def getSum(self, a: int, b: int) -> int:
# python没有溢出,需要特殊处理
x = 0xffffffff
a, b = a&x, b&x
while b != 0:
a, b = (a^b), ((a&b)<<1)&x
return a if a<= 0x7fffffff else ~(a^x)
注:取正数(补码)代表的负数补码的值
~(a^0xfff) 注:f格式视具体情况而定
1111 1111 1110 正数补码表示 4094
1111 1111 1110 负数补码表示 -2
>>>~(4094^0xfff)
-2
LeetCode 面试题 08.05. 递归乘法
https://leetcode.cn/problems/recursive-mulitply-lcci/
思路分析
得到A和B中最小值和最大值,将其中最小值当做乘数,选最小值当乘数,算的少
将乘数拆分成2的幂的和,即 $min= a0*2^0 + a1*2^1 + a2*2^2 +…+ an*2^n $ 其中ai取值为0或1
举例:
1312 = 13(8+4) = 13*(2^3 + 2^2) = 13<<3+13<<2
进一步简化
如上示例,需要左移5次,存在重复计算,可以进行简化
tmp = 13<<2 = 52
ans = tmp
tmp = tmp<<1 = 104
ans = ans+tmp
简化为执行3次左移和1次加法
代码实现
def multiply(A: int, B: int) -> int:
min_num = min(A, B)
max_num = max(A, B)
ans = 0
while min_num:
if min_num & 1:
ans += max_num
min_num >>= 1
max_num <<= 1 # 或者 max_num+=max_num
return ans
if __name__ == '__main__':
print(multiply(1, 10)) # 10
print(multiply(2, 10)) # 20
print(multiply(3, 4)) # 12
print(multiply(13, 12)) # 156
其他:python中的 bit_length() 方法
语法:int.bit_length()
说明:返回表示二进制整数的位数
print(7.bit_length()) # 3
print(-7.bit_length()) # 3
def multiply(A: int, B: int) -> int:
min_num = min(A, B)
max_num = max(A, B)
ans = 0
for i in range(min_num.bit_length()):
if min_num & 1:
ans += max_num
min_num >>= 1
max_num <<= 1
return ans