日常系列LeetCode《6·位运算篇》
Posted 常某某的好奇心
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了日常系列LeetCode《6·位运算篇》相关的知识,希望对你有一定的参考价值。
数据规模->时间复杂度
<=10^4 😮(n^2)
<=10^7:o(nlogn)
<=10^8:o(n)
10^8<=:o(logn),o(1)
基础:
注意:
正整数的补码=反码=原码
负整数的补码=反码+1=~原码+1
技巧:
lc 191【剑指 15】 :https://leetcode.cn/problems/number-of-1-bits/
提示:
输入必须是长度为 32 的 二进制串 。
进阶:
如果多次调用这个函数,你将如何优化你的算法?
#方案一:对每一位进行测试,看是否为1
class Solution:
def hammingWeight(self, n: int) -> int:
#o(1)
res=0
#<o(32)
for i in range(1,33):
if n&1<<(i-1) !=0:res+=1
#
return res
#方案二:不断右移n,判断最后一位是否为1
class Solution:
def hammingWeight(self, n: int) -> int:
#o(1)
res=0
#<o(32)
for i in range(32):
if n&1 !=0:
res+=1
n>>=1
#
return res
#方案三:每次移除掉最后一个1,直至n为0
class Solution:
def hammingWeight(self, n: int) -> int:
#o(1)
res=0
#<o(32)
while n !=0:
n&=n-1
res+=1
#
return res
lc 461 【top100】:https://leetcode.cn/problems/hamming-distance/
提示:
0 <= x, y <= 2^31 - 1
class Solution:
def hammingDistance(self, x: int, y: int) -> int:
#
res=0
s=x^y #异或
while s!=0:
s &=(s-1)#去1化
res+=1
#
return res
lc 477 :https://leetcode.cn/problems/total-hamming-distance/
提示:
1 <= nums.length <= 10^4
0 <= nums[i] <= 10^9
给定输入的对应答案符合 32-bit 整数范围
#不同位的汉明距离是相互独立的
#我们考虑数组中每个数二进制的第i位,假设一共有t个1 和 n - t个0
#,那么显然在第i位的汉明距离的总和为t * (n - t)
class Solution:
def totalHammingDistance(self, nums: List[int]) -> int:
#
n=len(nums)
s_um=0
counts=[0]*32 #统计各数字对应位上1的总个数
#o(32*n)
for num in nums:
i=0
while num!=0:
if num&1!=0:#判断:最后一位是否1
counts[i]+=1
i+=1#注意位置
num>>=1
#
for s in counts:
s_um+=s*(n-s)
return s_um
lc 231 :https://leetcode.cn/problems/power-of-two/
提示:
-2^31 <= n <= 2^31 - 1
进阶:你能够不使用循环/递归解决此问题吗?
#方案一:除数法
class Solution:
def isPowerOfTwo(self, n: int) -> bool:
if n==0:return False
while n%2==0:n//=2
return n==1
#方案二:位运算
#只要是2的幂的话,那么二进制中只有一个1
class Solution:
def isPowerOfTwo(self, n: int) -> bool:
if n==0:return False
return n&(n-1)==0#去掉最后一位1
lc 371 :https://leetcode.cn/problems/sum-of-two-integers/
提示:
-1000 <= a, b <= 1000
#异或运算:二进制的无进位加法
#与计算:计算出哪一位需要进位
class Solution:
def getSum(self, a: int, b: int) -> int:
#
MAX=0x7FFFFFFF #[0111 1111 1111 1111 1111 1111 1111 1111]最大正整数对应的二进制
MASK=0xFFFFFFFF #将整数二进制位固定至32位
#Python:整形二进制无固定位数
while b !=0:
a,b=(a^b)&MASK,((a&b)<<1)&MASK #非进位加法 #补进位
#返回:正整数或将负数的补码转成负数
return a if a<=MAX else ~(a^MASK) #key
lc 29【剑指 001】:https://leetcode.cn/problems/divide-two-integers/
提示:
被除数和除数均为 32 位有符号整数。
除数不为 0。
假设我们的环境只能存储 32 位有符号整数,其数值范围是 [−2^31, 2^31 − 1]。本题中,如果除法结果溢出,则返回 2^31 − 1。
#方案一:超时
class Solution:
def divide(self, dividend: int, divisor: int) -> int:
a,b=dividend,divisor
#注:边界
if a==-2**31 and b==-1:return 2**31-1
#o(n):减法代替除法,2**31-1->10^10(超时)
if (a>0)^(b>0):sign=-1
else: sign=1
#abs(-2**31)=-2**31(溢出)->(2**31-1)变负(不越界)
# a=abs(dividend)
# b=abs(divisor)
if a>0:a=-a
if b>0:b=-b
res=0
while a<=b:
a-=b
res+=1
#
return res if sign==1 else -res
#方案二:优化-每次尝试减去除数的倍数
#超时
class Solution:
def divide(self, dividend: int, divisor: int) -> int:
a,b=dividend,divisor
#① 注:边界
if a==-2**31 and b==-1:return 2**31-1
#② 符号
if (a>0)^(b>0):sign=-1
else: sign=1
#abs(-2**31)=-2**31(溢出)->(2**31-1)变负(不越界)
#③ 技巧:全转负数
if a>0:a=-a
if b>0:b=-b
#④ o(logn*logn):减去除数的倍数 a-b*2^k=0->k=log(a/b);a-2^t=0->t=log(a)
res=0
while a<=b:
value,k=b,1
while value>=0xc0000000 and a<=value+value: #保证vaue>=-2^30(这用十六进制表示),不越界
k+=k
value+=value
a-=value
res+=k
#
return res if sign==1 else -res
#方案三:再次优化:每次从最大位数开始尝试
#o(31)->o(1)
class Solution:
def divide(self, dividend: int, divisor: int) -> int:
a,b=dividend,divisor
#① 注:边界
if a==-2**31 and b==-1:return 2**31-1
#② 符号
sign=-1 if (a>0)^(b>0) else 1
#注:abs(-2**31)=-2**31(溢出)
a=abs(a)
b=abs(b)
#③ o(1)
res=0
for i in range(31,-1,-1):
if (a>>i)-b >= 0:
a-=b<<i
res+=1<<i
#a>=b<<i存在溢出越界问题
#无符号右移的目的是:将-2147483648看成2147483648
#如果 b = -2147483648,那么(α >>> i) >= b 永远为 true,但是(a >>> i) - b >=0为false
#
return res if sign==1 else -res
lc 136【top100】:https://leetcode.cn/problems/single-number/
说明:
你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?
#异或:类比哈希思想(有去除无添加)
class Solution:
def singleNumber(self, nums: List[int]) -> int:
#key:异或
#o(1)
base=0
#o(1)
for num in nums:
base^=num
#
return base
lc 137 【剑指 004】:https://leetcode.cn/problems/single-number-ii/
提示:
1 <= nums.length <= 3 * 10^4
-2^31 <= nums[i] <= 2^31 - 1
nums 中,除某个元素仅出现 一次 外,其余每个元素都恰出现 三次
进阶:你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?
#方案一:异或代替哈希查找
class Solution:
def singleNumber(self, nums: List[int]) -> int:
#o(n)
one=twice=0
for num in nums:
#one^num:one有则去除,无则结合twice情况是否增加
#~twice:无则,one增加
one=(one^num)& ~twice
twice=(twice^num) & ~one
#
return one
#方案二:统计每个数字指定位上1的个数
#如果1的个数不是3的倍数,说明那个只出现一次的数字的二进制位中在这一位是1
class Solution:
def singleNumber(self, nums: List[int]) -> int:
#o(n)
res=0
for i in range(32):
#统计:第i位
count=0
count=sum((num>>i)&1 for num in nums) #num第i位是否有1
#第i位的count
if count %3 !=0:
#注意:pothon 对【有符号整数类型】和【无符号整数类型】是没有区分,所以需要区分第 31 位,如果是 1 的话,那么需要减掉 -2^31 ?
if i==31:
res -=1<<i
else:res |= 1<<i
#
return res
lc 260:https://leetcode.cn/problems/single-number-iii/
提示:
2 <= nums.length <= 3 * 104
-231 <= nums[i] <= 231 - 1
除两个只出现一次的整数外,nums 中的其他数字都出现两次
进阶:你的算法应该具有线性时间复杂度。你能否仅使用常数空间复杂度来实现?
#[1,1,5]和[2,2,3]:分别对两个数组所有数字异或[ 3,5]
class Solution:
def singleNumber(self, nums: List[int]) -> List[int]:
#
mask=0
for num in nums:
mask ^=num
diff=mask&(-mask) #取末1:将两个只出现1次的数字划分到两组
#o(1),o(n)
res=[0]*2
for num in nums:
if num & diff !=0:
res[0]^=num
else: res[1]^=num
#
return res
lc 1318 :https://leetcode.cn/problems/minimum-flips-to-make-a-or-b-equal-to-c/
提示:
1 <= a <= 10^9
1 <= b <= 10^9
1 <= c <= 10^9
class Solution:
def minFlips(self, a: int, b: int, c: int) -> int:
#o(1)
diff=(a|b)^c #获得相异,需翻转的位
#o(1)
res=0
if diff==0:return 0
for i in range(32):
#相异:第i位
if (1<<i)&diff !=0:
#只有一种需要两次翻转的情况
if c&(1<<i)==0 and a&(1<<i)==b&(1<<i):
res+=2
else:
res+=1
#
return res
lc 201 :https://leetcode.cn/problems/bitwise-and-of-numbers-range/
提示:
0 <= left <= right <= 2^31 - 1
#方案一:右移
class Solution:
def rangeBitwiseAnd(self, left: int, right: int) -> int:
#o(32):公共部分
res=0
while left!=right:
left=left>>1
right=right>>1
res+=1
#还原
return left<<res
#方案二:去1
#不断的抹掉right的最后一个1,一直到right < left为止
class Solution:
def rangeBitwiseAnd(self, left: int, right: int) -> int:
#不断去掉最后一个1
while left<right:
right=right&(right-1)
#
return right
lc 476 :https://leetcode.cn/problems/number-complement/
提示:
1 <= num < 2^31
给定的整数num 保证在32位带符号整数的范围内
你可以假定二进制数不包含前导零位。
class Solution:
def findComplement(self, num: int) -> int:
#
mask=~0 #0xFFFFFFFF
#~mask:获得低位111
while (mask & num) !=0: mask<<=1
#
return ~mask ^ num
lc 405 :https://leetcode.cn/problems/convert-a-number-to-hexadecimal/
class Solution:
def toHex(self, num: int) -> str:
if num==0:return '0'
#
chars='0123456789abcdef'
res=''
#
#取后32位:python的整形二进制没有规定的位数
#每4位做一次转换
num=num & 0xFFFFFFFF
while num!=0:
res=chars[num&15]+res #15:0000 0000 0000 1111
num>>=4
#
return res
lc 190:https://leetcode.cn/problems/reverse-bits/
提示:
输入是一个长度为 32 的二进制字符串
进阶: 如果多次调用这个函数,你将如何优化你的算法?
#方案一:迭代法
class Solution:
def reverseBits(self, n: int) -> int:
#o(32)
res=0
for i in range(32):
res=(res<<1)|(n&1) #不断取最后一位并放于res
n>>=1 #更新n
#
return res
#方案二:分治法
class Solution:
def reverseBits(self, n: int) -> int:
#
m2=0x55555555 #0101 0101 0101 0101 ~
m4=0x33333333 #0011 0011 0011 0011 ~
m8=0x0f0f0f0f #0000 1111 0000 1111 ~
m16=0x00ff00ff #0000 0000 1111 1111 ~
#o(5)
# python 中没有 32 位的 int,我们需要将数字强转成 32 位 int
n = (n >> 1) & m2 | (n & m2) << 1 & 0xffffffff #每两位交换
n = ((n >> 2) & m4) | ((n & m4) << 2) & 0xffffffff
n = ((n >> 4) & m8) | ((n & m8) << 4) & 0xffffffff
n = ((n >> 8) & m16) | ((n & m16) << 8) & 0xffffffff
#
return (n>>16 | n<<16)& 0xffffffff
以上是关于日常系列LeetCode《6·位运算篇》的主要内容,如果未能解决你的问题,请参考以下文章