2025/7/6
题目(easy):
我的思路:
因为之前学了下《计算机组成原理》里面关于加法器的实现,通过逐位比对两个数是11,10,01,00以及搭配一位进位 x就可以处理所有的情况,所以这里我打算模拟这种竖式计算,即:
①先把两个字符补充到一样长(短的字符前补‘0’到和长的字符一样长)(保持a是最大的那个)
②分支判断条件决定这一位是0,还是1,以及下一位的进位值是0还是1,即可把每一位求出来
③最后再判断一下边界条件,即当到最高位的时候,是否还需要进位,如果还需要的话就要在前面插入一个1。
代码如下:
class Solution:
def addBinary(self, a: str, b: str) -> str:
#考虑竖式计算二进制的过程
#每一位两个二进制之间所有情况仅为11,10,01,00,再考虑一个进位是1还是0
#遍历是倒着遍历,以最短的字符串为遍历长度
if len(a) < len(b): #a总作为最大的数
a,b = b,a
b = '0'*(len(a)-len(b)) + b[0:]
x = '0' #进位符
a = list(a)
b = list(b)
for i in range(len(a)-1,-1,-1):
print(a[i],b[i],x)
if a[i] == '1' and b[i] == '1':
a[i] = x
x = '1'
elif (a[i] == '1' and b[i] == '0') or (a[i] == '0'and b[i] == '1'):
if x == '0':
a[i] = '1'
x = '0'
elif x == '1':
a[i] = '0'
x = '1'
elif a[i] == '0' and b[i] == '0':
a[i] = x
x = '0'
if i == 0 and x == '1':
a.insert(0,'1')
return ''.join(a)
时间复杂度:O(Max(m,n))【不论是交换位置,补全0,还是转化为数组,以及遍历判断,还有最后转化回字符串,它们的时间复杂度都是O(Max(m,n)),不过它们都是线性的,并没有相互嵌套,所以最后的时间复杂度还是O(Max(m,n))】
空间复杂度:O(1)【只用了几个临时变量】
优化思路:
①总感觉分支判断那里还是不够简洁,看着有点乱,可读性不够特别好。
②而且还有个问题就是其实每次计算的时候不一定要遍历完整个最长的字符串,在大多数情况下应该是可以提前终止结束的。
③a.insert(0,'1')这里很不好,因为是需要把所有元素都往后移,然后才能让他插入第一位,时间开销是O(n),如果能采用.append()会好一点
④查询了一下发现Python里好像是可以自动补前导0的,不用手动补0,也就是可以用b = b.zfill(len(a))
那要提前终止的话,就需要更细致考察每一个遍历到的位置,那自然又可以想到使用双指针的方法了,同时确定循环继续的条件为(i,j还没越界,而且进位值x != 0)的时候,以及循环提前终止的条件为(i,j至少有一个越界了,而且进位值 x == 0 )
代码如下:
class Solution:
def addBinary(self, a: str, b: str) -> str:
i = len(a)-1
j = len(b)-1
x = 0
res = []
while i>=0 or j >= 0 or x:
aNum = (int)(a[i]) if i >= 0 else 0
bNum = (int)(b[j]) if j >= 0 else 0
total = aNum + bNum + x
x = total // 2 #除法向下取整得到进位的值
res.append(str(total % 2)) #取余得到在二进制下的值(十进制转二进制原理)
i -= 1
j -= 1
return ''.join(reversed(res))
目前这里其实还没用到循环提前终止的条件(i,j至少有一个越界了,而且进位值 x == 0 )
但是在实际写的过程中发现,如果要让它提前终止的话,就需要再处理剩余的字符的拼接,这会导致代码变得更复杂,也就是又会要具体处理字符串了,这显然不是我们想要优化它可读性的初衷,所以还是不要提前终止了。目前双指针这样已经很简洁干净了
时间复杂度:O(Max(m,n))
空间复杂度:O(Max(m,n))
特别的思路:
1.自带方法秒了
class Solution:
def addBinary(self, a, b) -> str:
return '{0:b}'.format(int(a, 2) + int(b, 2))
#int(a,2)表示 将二进制字符串 a 转换成十进制整数
#'{0:b}'.format(...)
#'{0:b}' 是一个 Python 的格式化字符串,表示将数字格式化为二进制形式。
#format(...) 将相加后的十进制结果转换回二进制字符串。例如:
#6 → '110'(因为 6 的二进制是 110)
2.这是真加法器
class Solution:
def addBinary(self, a: str, b: str) -> str:
x, y = int(a, 2), int(b, 2) # 将二进制字符串转为整数
while y:
answer = x ^ y # 无进位相加(异或)
carry = (x & y) << 1 # 计算进位(与运算后左移1位)
x, y = answer, carry # 更新x和y,直到没有进位(y=0)
return bin(x)[2:] # 转回二进制字符串并去掉'0b'前缀
很巧妙地利用了异或是两个二进制的值【无进位的加法结果值】
而两个值取和是先求得这两个值中会导致进位的位(1 & 1 = 1),而再左移一位之后,刚好就能够位于它的【进位结果值】(比如1010 & 1000的时候得到1000,即从右到左第四位计算的时候需要进位,那进位自然就是进到从右到左第五位,所有左移一位得到正好就是进位值10000)
然后 【无进位的加法结果值】 ^ 【进位结果值】 = 新的【无进位的加法结果值】,然后再重复这个循环直到没有新的进位结果值即可。
总结:
①Python里字符串不能直接对它进行修改操作,所有能考虑不直接修改就先不要直接修改
②Python里补全前导0的方法有:
a = '0' * n + a[:0]
a = a.zFill(补全长度)
两种
a[i:]是指切片数组从i到之后的位置的数组
a[:i]是指切片数组从前面,到i的数组
注意:i 可以是负数,只是这样切片的顺序会反过来
③对于同时获取两个位的信息,双指针总是很有用,搭配一些两个数交互的原理,可以简化很多分支判断
④字符串的方法''.join(字符数组)可以把字符数组转化为字符串,(list)str又可以把字符串转化为数组,当需要具体修改字符串的时候可以这样操作变化
⑤bin(十进制值) 和 '{0:b}'.format(十进制值)都可以把十进制值转化为二进制字符串,区别在于bin(十进制值)转出来的字符串默认前缀是'0b','{0:b}'.format(十进制值)则没有这个前缀
⑥二进制值 ^ 二进制值 = 该二进制值的无进位加法值(显然当没有进位的时候,那它就等于二进制值+二进制值 了)
(二进制值 & 二进制值) << 1 = 该二进制值的进位值