Facebook Pixel

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.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

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:

  1. 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. Since 2 × (-2)^i = -1 × (-2)^(i+1), we carry -1 to the next position.

  2. 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, borrowing 1 from position i+1 gives us -2 at position i. Adding this to our -1 gives us -3, but we actually want 1 at the current position. So we set the current digit to 1 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:

  1. Initialize pointers and carry: Set i and j to point to the last elements of arr1 and arr2 respectively. Initialize carry c = 0 and an empty result list ans.

  2. 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 from arr1[i] (or 0 if out of bounds) and b from arr2[j] (or 0 if out of bounds)
    • Calculate the sum: x = a + b + c
    • Reset carry: c = 0
  3. 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 (because 2 × (-2)^i = -1 × (-2)^(i+1))
    • 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)
  4. Store result and move pointers:

    • Append x to ans
    • Decrement both pointers: i -= 1, j -= 1
  5. Remove leading zeros: After the loop, remove trailing zeros from ans (which become leading zeros when reversed), keeping at least one element.

  6. 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 Evaluator

Example 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 most max(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]
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

What are the most two important steps in writing a depth first search function? (Select 2)


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More