1073. Adding Two Negabinary Numbers
Problem Description
This problem asks you to add two numbers that are represented in base -2 (negabinary).
In base -2, each position represents a power of -2. For a number represented as an array [1,1,0,1]
, reading from left to right (most significant to least significant bit), it equals:
1 × (-2)³ + 1 × (-2)² + 0 × (-2)¹ + 1 × (-2)⁰
= 1 × (-8) + 1 × 4 + 0 × (-2) + 1 × 1
= -8 + 4 + 0 + 1
= -3
You're given two arrays arr1
and arr2
, each representing a number in base -2 format. The arrays contain only 0s and 1s, with the most significant bit at index 0. The arrays are guaranteed to have no leading zeros (except when representing 0 itself, which is [0]
).
Your task is to compute the sum of these two numbers and return the result in the same base -2 array format, also without leading zeros.
The solution uses a digit-by-digit addition approach similar to regular binary addition, but with special handling for the base -2 system. The key differences are:
- When the sum at a position is 2 or more, we need to subtract 2 from the current position and subtract 1 from the carry (because the next position represents
-2
times the current position's value) - When the sum is -1, we set the current digit to 1 and add 1 to the carry
The algorithm processes digits from right to left (least significant to most significant), handles the carry appropriately for base -2, and removes any leading zeros from the final result before returning it.
Intuition
The key insight is recognizing that addition in base -2 follows similar principles to regular binary addition, but with a twist in how we handle carries.
In regular binary (base 2), when we add two bits and get a sum ≥ 2, we write down sum % 2
and carry 1
to the next position. This works because each position represents a positive power of 2, so carrying 1
to the next position means adding 2^(i+1)
, which equals 2 × 2^i
.
In base -2, each position alternates between negative and positive powers. Position i
represents (-2)^i
. When we need to carry from position i
to position i+1
, we're moving from (-2)^i
to (-2)^(i+1) = -2 × (-2)^i
.
This creates two special cases:
-
When the sum is 2 or more: We have too many 1s at this position. To fix this, we subtract 2 from the current position (keeping
x - 2
) and need to account for those 2 units. Since2 × (-2)^i = -1 × (-2)^(i+1)
, we carry-1
to the next position. -
When the sum is -1: We can't have negative digits in our representation. To fix this, we borrow from the next position. Since
(-2)^(i+1) = -2 × (-2)^i
, borrowing1
from positioni+1
gives us-2
at positioni
. Adding this to our-1
gives us-3
, but we actually want1
at the current position. So we set the current digit to1
and carry+1
to compensate.
The algorithm naturally handles these cases by maintaining a carry value that can be positive, negative, or zero, adjusting it based on the sum at each position, and continuing until all digits and carries are processed.
Learn more about Math patterns.
Solution Approach
The implementation uses a two-pointer approach to traverse both arrays from right to left (least significant to most significant bit), similar to traditional addition algorithms.
Algorithm Steps:
-
Initialize pointers and carry: Set
i
andj
to point to the last elements ofarr1
andarr2
respectively. Initialize carryc = 0
and an empty result listans
. -
Main addition loop: Continue while there are digits to process or a carry exists (
i >= 0 or j >= 0 or c
):- Extract current digits:
a
fromarr1[i]
(or 0 if out of bounds) andb
fromarr2[j]
(or 0 if out of bounds) - Calculate the sum:
x = a + b + c
- Reset carry:
c = 0
- Extract current digits:
-
Handle special cases for base -2:
- If
x >= 2
: We have overflow at this position- Subtract 2 from current digit:
x -= 2
- Set negative carry:
c = -1
(because2 × (-2)^i = -1 × (-2)^(i+1)
)
- Subtract 2 from current digit:
- If
x == -1
: We have a negative digit which isn't allowed- Set current digit to 1:
x = 1
- Set positive carry:
c = 1
(to compensate for the adjustment)
- Set current digit to 1:
- If
-
Store result and move pointers:
- Append
x
toans
- Decrement both pointers:
i -= 1
,j -= 1
- Append
-
Remove leading zeros: After the loop, remove trailing zeros from
ans
(which become leading zeros when reversed), keeping at least one element. -
Return reversed result: Since we built the answer from least to most significant bit, reverse it before returning:
ans[::-1]
Data Structure: The solution uses a simple list to build the result, which is efficient for appending elements and allows easy reversal at the end.
Time Complexity: O(max(n, m))
where n
and m
are the lengths of the input arrays.
Space Complexity: O(max(n, m))
for the result array.
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's add [1,0,1]
and [1,1,1]
in base -2.
First, let's verify what these numbers represent:
[1,0,1]
= 1×(-2)² + 0×(-2)¹ + 1×(-2)⁰ = 4 + 0 + 1 = 5[1,1,1]
= 1×(-2)² + 1×(-2)¹ + 1×(-2)⁰ = 4 + (-2) + 1 = 3- Expected sum: 5 + 3 = 8
Now let's trace through the algorithm:
Initial Setup:
i = 2
,j = 2
(pointing to last elements)c = 0
(carry)ans = []
Iteration 1 (rightmost position, (-2)⁰):
a = arr1[2] = 1
,b = arr2[2] = 1
x = a + b + c = 1 + 1 + 0 = 2
- Since
x >= 2
:x = x - 2 = 0
c = -1
(negative carry)
ans = [0]
- Move pointers:
i = 1
,j = 1
Iteration 2 (middle position, (-2)¹):
a = arr1[1] = 0
,b = arr2[1] = 1
x = a + b + c = 0 + 1 + (-1) = 0
- No special case needed
c = 0
ans = [0, 0]
- Move pointers:
i = 0
,j = 0
Iteration 3 (leftmost position, (-2)²):
a = arr1[0] = 1
,b = arr2[0] = 1
x = a + b + c = 1 + 1 + 0 = 2
- Since
x >= 2
:x = x - 2 = 0
c = -1
ans = [0, 0, 0]
- Move pointers:
i = -1
,j = -1
Iteration 4 (new position, (-2)³):
a = 0
(out of bounds),b = 0
(out of bounds)x = 0 + 0 + (-1) = -1
- Since
x == -1
:x = 1
c = 1
ans = [0, 0, 0, 1]
- Move pointers:
i = -2
,j = -2
Iteration 5 (new position, (-2)⁴):
a = 0
,b = 0
x = 0 + 0 + 1 = 1
- No special case needed
c = 0
ans = [0, 0, 0, 1, 1]
- Loop ends (no more digits and carry is 0)
Final Steps:
- Remove leading zeros:
ans = [0, 0, 0, 1, 1]
(no trailing zeros to remove) - Reverse:
ans = [1, 1, 0, 0, 0]
Verification:
[1,1,0,0,0]
= 1×(-2)⁴ + 1×(-2)³ + 0×(-2)² + 0×(-2)¹ + 0×(-2)⁰ = 16 + (-8) + 0 + 0 + 0 = 8 ✓
Solution Implementation
1class Solution:
2 def addNegabinary(self, arr1: List[int], arr2: List[int]) -> List[int]:
3 # Initialize pointers to the least significant bits (rightmost elements)
4 index1 = len(arr1) - 1
5 index2 = len(arr2) - 1
6
7 # Initialize carry value for addition
8 carry = 0
9
10 # Result list to store the sum digits
11 result = []
12
13 # Process digits from right to left, including any remaining carry
14 while index1 >= 0 or index2 >= 0 or carry != 0:
15 # Get current digit from arr1, or 0 if we've exhausted arr1
16 digit1 = 0 if index1 < 0 else arr1[index1]
17
18 # Get current digit from arr2, or 0 if we've exhausted arr2
19 digit2 = 0 if index2 < 0 else arr2[index2]
20
21 # Calculate sum of current position including carry
22 current_sum = digit1 + digit2 + carry
23
24 # Reset carry for next iteration
25 carry = 0
26
27 # Handle negabinary addition rules
28 if current_sum >= 2:
29 # If sum is 2 or more, subtract 2 and set negative carry
30 current_sum -= 2
31 carry = -1
32 elif current_sum == -1:
33 # If sum is -1, set digit to 1 and positive carry
34 current_sum = 1
35 carry = 1
36
37 # Append the computed digit to result
38 result.append(current_sum)
39
40 # Move pointers to the next more significant digits
41 index1 -= 1
42 index2 -= 1
43
44 # Remove leading zeros from the result (except if result is just [0])
45 while len(result) > 1 and result[-1] == 0:
46 result.pop()
47
48 # Reverse the result since we built it from least to most significant
49 return result[::-1]
50
1class Solution {
2 public int[] addNegabinary(int[] arr1, int[] arr2) {
3 // Initialize pointers to the least significant bits (rightmost elements)
4 int pointer1 = arr1.length - 1;
5 int pointer2 = arr2.length - 1;
6
7 // List to store the result digits
8 List<Integer> result = new ArrayList<>();
9
10 // Process digits from right to left with carry handling
11 // Continue while there are digits to process or carry exists
12 for (int carry = 0; pointer1 >= 0 || pointer2 >= 0 || carry != 0; pointer1--, pointer2--) {
13 // Get current digits, use 0 if index is out of bounds
14 int digit1 = pointer1 < 0 ? 0 : arr1[pointer1];
15 int digit2 = pointer2 < 0 ? 0 : arr2[pointer2];
16
17 // Calculate sum of current position including carry
18 int sum = digit1 + digit2 + carry;
19
20 // Reset carry for next iteration
21 carry = 0;
22
23 // Handle different sum cases for negabinary addition
24 if (sum >= 2) {
25 // If sum is 2 or 3, subtract 2 and set negative carry
26 // In negabinary: 2 = 110 (-2 + 2 + 0), 3 = 111 (-2 + 2 + 1)
27 sum -= 2;
28 carry = -1;
29 } else if (sum == -1) {
30 // If sum is -1, it becomes 1 with positive carry
31 // In negabinary: -1 = 11 (2 + (-1))
32 sum = 1;
33 carry = 1;
34 }
35
36 // Add the resulting digit to the result list
37 result.add(sum);
38 }
39
40 // Remove leading zeros (except if result is just [0])
41 while (result.size() > 1 && result.get(result.size() - 1) == 0) {
42 result.remove(result.size() - 1);
43 }
44
45 // Reverse to get most significant bit first
46 Collections.reverse(result);
47
48 // Convert List<Integer> to int[] and return
49 return result.stream().mapToInt(digit -> digit).toArray();
50 }
51}
52
1class Solution {
2public:
3 vector<int> addNegabinary(vector<int>& arr1, vector<int>& arr2) {
4 // Initialize pointers to the least significant bits (rightmost elements)
5 int index1 = arr1.size() - 1;
6 int index2 = arr2.size() - 1;
7
8 // Result vector to store the sum
9 vector<int> result;
10
11 // Carry value for negabinary addition
12 int carry = 0;
13
14 // Process digits from right to left while there are digits or carry
15 while (index1 >= 0 || index2 >= 0 || carry != 0) {
16 // Get current digits (0 if index out of bounds)
17 int digit1 = (index1 >= 0) ? arr1[index1] : 0;
18 int digit2 = (index2 >= 0) ? arr2[index2] : 0;
19
20 // Calculate sum of current position including carry
21 int sum = digit1 + digit2 + carry;
22
23 // Reset carry for next iteration
24 carry = 0;
25
26 // Handle negabinary arithmetic rules
27 if (sum >= 2) {
28 // If sum is 2 or 3, subtract 2 and set negative carry
29 sum -= 2;
30 carry = -1;
31 } else if (sum == -1) {
32 // If sum is -1, set digit to 1 and positive carry
33 sum = 1;
34 carry = 1;
35 }
36
37 // Add the computed digit to result
38 result.push_back(sum);
39
40 // Move to next digits (more significant bits)
41 index1--;
42 index2--;
43 }
44
45 // Remove leading zeros (except if result is just [0])
46 while (result.size() > 1 && result.back() == 0) {
47 result.pop_back();
48 }
49
50 // Reverse to get most significant bit first
51 reverse(result.begin(), result.end());
52
53 return result;
54 }
55};
56
1/**
2 * Adds two numbers represented in negabinary (base -2) format
3 * @param arr1 - First negabinary number as array of digits (least significant digit last)
4 * @param arr2 - Second negabinary number as array of digits (least significant digit last)
5 * @returns The sum of arr1 and arr2 in negabinary format
6 */
7function addNegabinary(arr1: number[], arr2: number[]): number[] {
8 // Initialize pointers to the least significant digits (rightmost)
9 let index1: number = arr1.length - 1;
10 let index2: number = arr2.length - 1;
11
12 // Array to store the result digits
13 const result: number[] = [];
14
15 // Process digits from right to left with carry
16 let carry: number = 0;
17
18 while (index1 >= 0 || index2 >= 0 || carry !== 0) {
19 // Get current digits or 0 if index is out of bounds
20 const digit1: number = index1 < 0 ? 0 : arr1[index1];
21 const digit2: number = index2 < 0 ? 0 : arr2[index2];
22
23 // Calculate sum of current position including carry
24 let currentSum: number = digit1 + digit2 + carry;
25
26 // Reset carry for next iteration
27 carry = 0;
28
29 // Handle negabinary arithmetic rules
30 if (currentSum >= 2) {
31 // If sum is 2 or more, subtract 2 and set carry to -1
32 currentSum -= 2;
33 carry = -1;
34 } else if (currentSum === -1) {
35 // If sum is -1, set digit to 1 and carry to 1
36 currentSum = 1;
37 carry = 1;
38 }
39
40 // Add current digit to result
41 result.push(currentSum);
42
43 // Move to next significant digits
44 index1--;
45 index2--;
46 }
47
48 // Remove leading zeros (except if result is just [0])
49 while (result.length > 1 && result[result.length - 1] === 0) {
50 result.pop();
51 }
52
53 // Reverse to get most significant digit first
54 return result.reverse();
55}
56
Time and Space Complexity
Time Complexity: O(max(n, m))
where n
is the length of arr1
and m
is the length of arr2
.
The algorithm iterates through both arrays from right to left once. The main while loop continues until both indices i
and j
become negative and there's no carry c
. In the worst case, this happens after processing all elements from both arrays plus potentially a few extra iterations for carry propagation. The number of iterations is bounded by max(n, m) + constant
, which simplifies to O(max(n, m))
.
The second while loop that removes trailing zeros runs at most O(max(n, m))
times since the result array can't be longer than max(n, m) + constant
elements.
The final reversal operation ans[::-1]
takes O(len(ans))
time, which is also bounded by O(max(n, m))
.
Space Complexity: O(max(n, m))
The algorithm uses:
- Variables
i
,j
,c
,a
,b
,x
:O(1)
space - The
ans
list: stores the result digits, which in the worst case can be at mostmax(n, m) + 1
elements (when there's a carry that extends the result by one position) - The reversed array returned at the end creates a new list of the same size
Therefore, the total space complexity is O(max(n, m))
.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Incorrect Carry Handling in Base -2
The most common mistake is treating this like regular binary addition where carry is always positive. In base -2, the carry logic is counterintuitive:
Wrong approach:
if current_sum >= 2: current_sum -= 2 carry = 1 # WRONG! Should be -1
Why it's wrong: In base -2, when you have a sum of 2 at position i, it equals 2 × (-2)^i
. To carry this to position i+1, you need to recognize that 2 × (-2)^i = -1 × (-2)^(i+1)
, hence the carry should be -1, not 1.
Correct approach:
if current_sum >= 2: current_sum -= 2 carry = -1 # Correct negative carry
2. Forgetting to Handle Negative Sum
Another pitfall is not handling the case when the sum becomes -1:
Wrong approach:
# Only handling the >= 2 case if current_sum >= 2: current_sum -= 2 carry = -1 # Missing the -1 case!
Why it's wrong: In base -2, we can only have digits 0 or 1. When the sum is -1 (which can happen with a negative carry), we must convert it to a valid digit.
Correct approach:
if current_sum >= 2: current_sum -= 2 carry = -1 elif current_sum == -1: current_sum = 1 carry = 1
3. Premature Loop Termination
Stopping the loop too early when both arrays are exhausted but carry still exists:
Wrong approach:
while index1 >= 0 or index2 >= 0: # WRONG! Ignores remaining carry # process digits
Why it's wrong: The carry might propagate beyond the length of both input arrays, potentially adding more significant digits to the result.
Correct approach:
while index1 >= 0 or index2 >= 0 or carry != 0: # Include carry in condition # process digits
4. Leading Zeros in Result
Forgetting to remove leading zeros or removing too many:
Wrong approach:
# Forgetting to remove leading zeros entirely return result[::-1] # Or removing all zeros (including the case where answer is 0) while result[-1] == 0: result.pop()
Correct approach:
# Keep at least one element to handle the case where the sum is 0
while len(result) > 1 and result[-1] == 0:
result.pop()
return result[::-1]
What are the most two important steps in writing a depth first search function? (Select 2)
Recommended Readings
Math for Technical Interviews How much math do I need to know for technical interviews The short answer is about high school level math Computer science is often associated with math and some universities even place their computer science department under the math faculty However the reality is that you
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Want a Structured Path to Master System Design Too? Don’t Miss This!