LeetCode 1073. 负二进制数相加
题目描述


题意分析
两个数组按从高位到低位保存负二进制数,每位只能为 0 或 1,第
k位的权重是 $(-2)^k$。求两数之和并按相同格式返回,不能有多余前导零;结果为零时返回一位 0。
解法:进位模拟
核心思路
[!blue]
仍然从最低位开始逐位相加,用两个下标取当前输入位,缺少的一位按 0 处理,再加上低一位传来的
carry,得到当前总量sum。区别只在于,高一位的权重是当前位的 -2 倍,而不是 2 倍。设当前结果位为
bit、传给高一位的新进位为nextCarry,必须满足sum = bit - 2 * nextCarry。让bit取与sum同奇偶的 0 或 1,就能得到整数进位nextCarry = -(sum - bit) / 2,每一步都保持数值不变。进位可能为负。用
bit = sum & 1可在sum为负数时仍得到合法的 0 或 1;直接取% 2则可能得到 -1。进位初始为 0,递推中始终属于 -1、0、1,所以sum只可能在 -1 到 3 之间。输入耗尽后,进位 1 下一轮变成 0,进位 -1 最多经过 -1 到 1 再到 0,两轮就能收尾。结果按低位到高位追加。完成进位后,列表末尾才是高位,先从末尾删掉多余的 0,再反转成题目要求的高位在前顺序;删除时至少保留一位。
解题步骤
- 两个下标从各自数组末尾开始,初始化
carry = 0和结果列表res。- 只要任一输入还有位,或
carry != 0,就将当前输入位与进位相加。- 把
bit = sum & 1追加到res,再令carry = -(sum - bit) / 2。- 当结果长度大于 1 且末位为 0 时,删除末位,去掉高位的多余零。
- 反转结果,返回从高位到低位排列的数组。
代码实现
class Solution {
public int[] addNegabinary(int[] arr1, int[] arr2) {
int i = arr1.length - 1;
int j = arr2.length - 1;
int carry = 0;
List<Integer> res = new ArrayList<>();
while (i >= 0 || j >= 0 || carry != 0) {
int sum = carry;
if (i >= 0) {
sum += arr1[i--];
}
if (j >= 0) {
sum += arr2[j--];
}
// 负总量也要取合法的零或一,不能直接用负余数。
int bit = sum & 1;
res.add(bit);
// 高一位权重是负二倍,移到高位的量因此带负号。
carry = -(sum - bit) / 2;
}
// 只去掉多余高位零,零值仍保留一位。
while (res.size() > 1 && res.get(res.size() - 1) == 0) {
res.remove(res.size() - 1);
}
int[] answer = new int[res.size()];
for (int k = 0; k < res.size(); k++) {
answer[k] = res.get(res.size() - 1 - k);
}
return answer;
}
}
func addNegabinary(arr1 []int, arr2 []int) []int {
i := len(arr1) - 1
j := len(arr2) - 1
carry := 0
res := make([]int, 0)
for i >= 0 || j >= 0 || carry != 0 {
sum := carry
if i >= 0 {
sum += arr1[i]
i--
}
if j >= 0 {
sum += arr2[j]
j--
}
// 负总量也要取合法的零或一,不能直接用负余数。
bit := sum & 1
res = append(res, bit)
// 高一位权重是负二倍,移到高位的量因此带负号。
carry = -(sum - bit) / 2
}
// 只去掉多余高位零,零值仍保留一位。
for len(res) > 1 && res[len(res)-1] == 0 {
res = res[:len(res)-1]
}
for l, r := 0, len(res)-1; l < r; l, r = l+1, r-1 {
res[l], res[r] = res[r], res[l]
}
return res
}
复杂度分析
- 时间复杂度:$O(\max(m,n))$,其中
m、n是两个输入数组的长度;逐位相加后最多再处理两轮进位,去零和反转也为线性。- 空间复杂度:结果需要 $O(\max(m,n))$ 空间;不计返回值时,Java 的中间列表仍占 $O(\max(m,n))$,Go 原地反转结果缓冲,额外空间为 $O(1)$。
关键点总结
[!green]
- 进位可以为负,不能套普通二进制加法。
- 循环还需处理输入耗尽后的非零进位。
易错点总结
[!yellow]
sum对 2 取余可能产生 -1,违反每位只能为 0 或 1 的要求。- 进位漏负号会把结果当正二进制处理。
- 清掉全部零会返回空数组,而零应有一位表示。
相似题目
| 题目 | 难度 | 关联与区别 |
|---|---|---|
| 67. 二进制求和 | 简单 | 逐位加法框架相同,但本题基数为-2,进位可能为负,不能照搬普通二进制进位。 |
转载与许可
许可
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!