1. 二进制求和问题概述
今天我们来聊聊LeetCode第67题"二进制求和",这是一道被标记为"简单"级别的字符串操作题目。虽然难度不高,但作为二进制运算的基础题目,它在面试中的出现频率相当可观。根据我的刷题经验,这道题在2023年的字节跳动、腾讯等公司的初级岗位面试中出现了至少5次。
题目要求给定两个二进制字符串(如"1010"和"1011"),返回它们的和(用二进制表示)。例如:
输入: a = "11", b = "1"
输出: "100"
表面看这题很简单,但实际编码时会遇到几个典型陷阱:
- 字符串长度不一致时的处理
- 最高位进位的情况
- 前导零的处理
- 性能优化问题
2. 问题分析与解法思路
2.1 基础解法:模拟竖式加法
最直观的解法是模拟我们手工计算二进制加法的过程。具体步骤包括:
- 将两个字符串右对齐
- 从最低位(字符串末尾)开始逐位相加
- 处理进位(carry)
- 最后检查最高位是否有进位
这个解法的时间复杂度是O(max(M,N)),其中M和N分别是两个字符串的长度。空间复杂度也是O(max(M,N)),用于存储结果。
2.2 优化思路:提前处理字符串
在实际编码中,我发现可以先对较短的字符串进行前导零填充,使两个字符串长度相同。这样可以简化循环中的条件判断。例如:
python复制max_len = max(len(a), len(b))
a = a.zfill(max_len)
b = b.zfill(max_len)
注意:zfill()方法会在字符串左侧填充零,直到达到指定长度。这在处理不等长字符串时特别有用。
3. 代码实现与逐行解析
3.1 Python实现版本
python复制def addBinary(a: str, b: str) -> str:
carry = 0
result = []
# 使两个字符串等长
max_len = max(len(a), len(b))
a = a.zfill(max_len)
b = b.zfill(max_len)
# 从最低位开始相加
for i in range(max_len-1, -1, -1):
sum_bits = int(a[i]) + int(b[i]) + carry
carry = sum_bits // 2
result.append(str(sum_bits % 2))
# 处理最高位进位
if carry:
result.append(str(carry))
# 反转结果并拼接成字符串
return ''.join(reversed(result))
3.2 关键代码解析
- carry变量:记录进位值,初始为0
- zfill处理:确保两个字符串长度一致,避免越界访问
- 循环逻辑:
- 从字符串末尾开始遍历(range(max_len-1, -1, -1))
- 将字符转换为整数后相加(int(a[i]) + int(b[i]) + carry)
- 计算新的进位值(sum_bits // 2)
- 记录当前位的结果(sum_bits % 2)
- 最终进位处理:循环结束后检查是否还有进位
- 结果反转:因为我们是从低位开始计算,所以需要反转结果列表
4. 边界条件与测试用例
4.1 必须考虑的边界情况
根据我的实战经验,这道题有以下几个关键边界条件需要测试:
- 两个空字符串:理论上应该返回"0",但LeetCode的测试用例中不会出现
- 长度差异大的字符串:如"1"和"11111111111111111111"
- 产生连续进位的情况:如"111"+"1"="1000"
- 包含前导零的输入:如"001"+"01"="10"
4.2 推荐测试用例
python复制测试用例示例:
1. ("11", "1") → "100"
2. ("1010", "1011") → "10101"
3. ("0", "0") → "0"
4. ("1111", "1111") → "11110"
5. ("1", "11111111111111111111") → "100000000000000000000"
提示:在面试中,主动提出这些边界条件并解释如何处理,会给面试官留下好印象。
5. 算法优化与进阶思考
5.1 不使用zfill的优化版本
虽然zfill使代码更简洁,但创建新字符串会有额外空间开销。我们可以通过双指针技巧来优化:
python复制def addBinary(a: str, b: str) -> str:
i, j = len(a)-1, len(b)-1
carry = 0
result = []
while i >= 0 or j >= 0 or carry:
val_a = int(a[i]) if i >= 0 else 0
val_b = int(b[j]) if j >= 0 else 0
sum_bits = val_a + val_b + carry
carry = sum_bits // 2
result.append(str(sum_bits % 2))
i -= 1
j -= 1
return ''.join(reversed(result))
这个版本避免了创建临时字符串,空间效率更高。
5.2 位运算解法(理论探讨)
理论上,二进制加法可以用位运算实现:
- 先将字符串转为整数
- 使用异或运算得到无进位和
- 使用与运算和左移得到进位
- 循环直到进位为0
但在Python中,这种解法对大数不适用(因为Python的整数没有固定位数限制),而且题目明确要求字符串输入输出,所以实际面试中不建议使用。
6. 常见错误与调试技巧
6.1 新手常犯的错误
- 忘记处理最高位进位:如"1"+"1"="0"(正确应为"10")
- 字符串反转错误:append是顺序添加,最后需要反转
- 类型混淆:忘记将字符转换为整数进行运算
- 索引越界:处理不等长字符串时访问不存在的索引
6.2 调试建议
当你的代码出现问题时,可以:
- 打印每一步的计算结果:
python复制print(f"i={i}, a[i]={a[i]}, b[i]={b[i]}, carry={carry}, sum={sum_bits}") - 使用小测试用例手动模拟执行过程
- 特别注意循环的起始和终止条件
7. 实际应用场景延伸
虽然这看起来只是一道算法题,但二进制求和的实际应用场景很广泛:
- 计算机体系结构:ALU中的加法器实现原理
- 加密算法:某些加密操作需要精确的位运算
- 网络协议:如IP地址计算、校验和计算
- 数据库存储:位图索引的操作基础
理解这个简单问题的底层原理,有助于我们在这些领域进行更深入的开发工作。
8. 同类题目推荐
为了巩固二进制操作的能力,我推荐练习以下LeetCode题目:
-
- 两整数之和(不用加减乘除做加法)
-
- 两数相除(位运算实现除法)
-
- 位1的个数(计算汉明重量)
-
- 只出现一次的数字(异或运算的巧妙应用)
这些题目都能帮助你建立对位运算的直觉,在面试中遇到位操作相关题目时能够快速反应。
