LeetCode 1073 - Adding Two Negabinary Numbers
The problem asks us to add two numbers that are represented in negabinary, which is base -2. Unlike standard binary (base 2), each bit in negabinary contributes a value multiplied by powers of -2.
Difficulty: 🟡 Medium
Topics: Array, Math
Solution
Problem Understanding
The problem asks us to add two numbers that are represented in negabinary, which is base -2. Unlike standard binary (base 2), each bit in negabinary contributes a value multiplied by powers of -2. For example, the array [1,1,0,1] represents:
1 * (-2)^3 + 1 * (-2)^2 + 0 * (-2)^1 + 1 * (-2)^0
= -8 + 4 + 0 + 1
= -3
The input consists of two arrays arr1 and arr2, each containing only 0 or 1 values, representing numbers without leading zeros (except for [0]). The output must also be in the same negabinary array format without leading zeros.
The constraints indicate that arrays can be up to length 1000, so any approach that converts the arrays to a standard integer type and then adds may fail due to potential overflow, especially in languages with fixed-width integers. We must therefore design a solution that handles the arithmetic directly in negabinary form.
Edge cases to consider include adding [0] with [0], adding [0] with a non-zero number, and arrays of different lengths. Negabinary addition rules differ from standard binary, especially because carries can be negative and propagate differently.
Approaches
Brute Force
A naive approach is to convert both negabinary arrays to decimal integers, perform standard addition, and then convert the result back to negabinary. This works correctly because the mathematical definition of negabinary allows conversion to decimal and back. However, with arrays of length 1000, the decimal value can become extremely large, potentially exceeding standard integer limits and incurring significant computational overhead.
Optimal Approach
The optimal approach performs direct negabinary addition without converting to decimal. The key observation is that in base -2, the carry rules differ from standard binary. We add corresponding bits from the least significant end and manage carries using the formula:
total = bit1 + bit2 + carry
new_bit = total & 1 # bit value is modulo 2
carry = -(total >> 1) # carry is negative half of total
This method handles both positive and negative carries automatically. By processing from the least significant bit to the most, we build the result efficiently.
| Approach | Time Complexity | Space Complexity | Notes |
|---|---|---|---|
| Brute Force | O(n + m) conversion + O(log N) addition | O(n + m) | Convert to decimal, add, convert back; may overflow |
| Optimal | O(max(n, m)) | O(max(n, m)) | Direct negabinary addition using carry propagation |
Algorithm Walkthrough
- Reverse both input arrays so we can process from least significant bit to most.
- Initialize a result list and set
carry = 0. - Iterate through indices up to the maximum length of the two arrays.
- For each index, sum the corresponding bits from both arrays plus the carry.
- Compute the new bit as
total % 2and append to the result. - Update the carry as
-(total // 2). This handles negative carries correctly due to the base -2. - After finishing all bits, continue processing if
carryis not zero, following the same rules. - Reverse the result list to restore the most significant bit first.
- Remove any leading zeros unless the result is
[0].
Why it works: This algorithm maintains the invariant that at each step, the sum of the processed bits plus the carry represents the correct total in negabinary. The special carry formula ensures that negative contributions propagate correctly, yielding a correct final result.
Python Solution
from typing import List
class Solution:
def addNegabinary(self, arr1: List[int], arr2: List[int]) -> List[int]:
i, j = len(arr1) - 1, len(arr2) - 1
carry = 0
result = []
while i >= 0 or j >= 0 or carry != 0:
total = carry
if i >= 0:
total += arr1[i]
i -= 1
if j >= 0:
total += arr2[j]
j -= 1
# Compute new bit and carry
result.append(total & 1)
carry = -(total >> 1)
# Remove leading zeros
while len(result) > 1 and result[-1] == 0:
result.pop()
return result[::-1]
Implementation Explanation: We iterate from the least significant bit to the most significant, summing the corresponding bits and the carry. The bitwise operations &1 and >>1 efficiently compute the new bit and carry. Leading zeros are removed after the calculation, and we reverse the result for correct MSB-to-LSB ordering.
Go Solution
func addNegabinary(arr1 []int, arr2 []int) []int {
i, j := len(arr1)-1, len(arr2)-1
carry := 0
result := []int{}
for i >= 0 || j >= 0 || carry != 0 {
total := carry
if i >= 0 {
total += arr1[i]
i--
}
if j >= 0 {
total += arr2[j]
j--
}
result = append(result, total & 1)
carry = -(total >> 1)
}
// Remove leading zeros
for len(result) > 1 && result[len(result)-1] == 0 {
result = result[:len(result)-1]
}
// Reverse the result
for k, n := 0, len(result); k < n/2; k++ {
result[k], result[n-1-k] = result[n-1-k], result[k]
}
return result
}
Go Implementation Notes: Go does not have a built-in reverse, so we manually reverse the slice. The carry and bit calculations mirror the Python implementation. Slice appending is used to dynamically build the result.
Worked Examples
Example 1: arr1 = [1,1,1,1,1], arr2 = [1,0,1]
| Step | i | j | total | new bit | carry | result (reversed) |
|---|---|---|---|---|---|---|
| 1 | 4 | 2 | 1+1+0=2 | 0 | -1 | [0] |
| 2 | 3 | 1 | 1+0+(-1)=0 | 0 | 0 | [0,0] |
| 3 | 2 | 0 | 1+1+0=2 | 0 | -1 | [0,0,0] |
| 4 | 1 | -1 | 1+0+(-1)=0 | 0 | 0 | [0,0,0,0] |
| 5 | 0 | -1 | 1+0+0=1 | 1 | 0 | [0,0,0,0,1] |
Reverse to [1,0,0,0,0].
Example 2: arr1 = [0], arr2 = [0] → result [0] after trivial addition.
Example 3: arr1 = [0], arr2 = [1] → result [1].
Complexity Analysis
| Measure | Complexity | Explanation |
|---|---|---|
| Time | O(max(n, m)) | We iterate through the longer array once, handling all bits and carry. |
| Space | O(max(n, m)) | The result array may grow to at most one more than the longer input array due to carry propagation. |
The algorithm scales linearly with input size and avoids large integer arithmetic, making it safe for large inputs.
Test Cases
# Provided examples
assert Solution().addNegabinary([1,1,1,1,1], [1,0,1]) == [1,0,0,0,0] # normal addition
assert Solution().addNegabinary([0], [0]) == [0] # both zeros
assert Solution().addNegabinary([0], [1]) == [1] # zero + one
# Edge tests
assert Solution().addNegabinary([1], [1]) == [1,1,0] # 1+1 in negabinary
assert Solution().addNegabinary([1,0,1], [1,0,1]) == [1,1,1,1,0] # same length addition
assert Solution().addNegabinary([1,1,0,1], [0]) == [1,1,0,1] # adding zero
assert Solution().addNegabinary([1,1,1,1], [1]) == [1,0,0,0,1,1] # carry propagation
| Test | Why |
|---|---|
| [1,1,1,1,1] + [1,0,1] | Normal addition with multiple carries |
| [0] + [0] | Both zeros, trivial output |
| [ |