questions A (01-matrix - avoid-flood)
This commit is contained in:
224
backend/data/questions/adding-two-negabinary-numbers.yaml
Normal file
224
backend/data/questions/adding-two-negabinary-numbers.yaml
Normal file
@@ -0,0 +1,224 @@
|
||||
title: Adding Two Negabinary Numbers
|
||||
slug: adding-two-negabinary-numbers
|
||||
difficulty: medium
|
||||
leetcode_id: 1073
|
||||
leetcode_url: https://leetcode.com/problems/adding-two-negabinary-numbers/
|
||||
categories:
|
||||
- arrays
|
||||
- math
|
||||
patterns:
|
||||
- two-pointers
|
||||
|
||||
description: |
|
||||
Given two numbers `arr1` and `arr2` in base **-2** (negabinary), return the result of adding them together.
|
||||
|
||||
Each number is given in *array format*: as an array of `0`s and `1`s, from most significant bit to least significant bit. For example, `arr = [1,1,0,1]` represents the number `(-2)^3 + (-2)^2 + (-2)^0 = -3`.
|
||||
|
||||
A number `arr` in array format is also guaranteed to have no leading zeros: either `arr == [0]` or `arr[0] == 1`.
|
||||
|
||||
Return *the result of adding `arr1` and `arr2` in the same format*: as an array of `0`s and `1`s with no leading zeros.
|
||||
|
||||
constraints: |
|
||||
- `1 <= arr1.length, arr2.length <= 1000`
|
||||
- `arr1[i]` and `arr2[i]` are `0` or `1`
|
||||
- `arr1` and `arr2` have no leading zeros
|
||||
|
||||
examples:
|
||||
- input: "arr1 = [1,1,1,1,1], arr2 = [1,0,1]"
|
||||
output: "[1,0,0,0,0]"
|
||||
explanation: "arr1 represents 11 in decimal, arr2 represents 5, and the output represents 16."
|
||||
- input: "arr1 = [0], arr2 = [0]"
|
||||
output: "[0]"
|
||||
explanation: "Both inputs are zero, so the sum is zero."
|
||||
- input: "arr1 = [0], arr2 = [1]"
|
||||
output: "[1]"
|
||||
explanation: "0 + 1 = 1."
|
||||
|
||||
explanation:
|
||||
intuition: |
|
||||
Think of this like elementary school addition, but with a twist: the base is **-2** instead of 10.
|
||||
|
||||
In standard binary (base 2), when you add two `1`s, you get `2`, which means you write `0` and carry `1` to the next position. But in negabinary (base **-2**), the rules are different because each position alternates between positive and negative powers.
|
||||
|
||||
The key insight is understanding what happens with carries. In base -2:
|
||||
- Position 0 has weight `(-2)^0 = 1`
|
||||
- Position 1 has weight `(-2)^1 = -2`
|
||||
- Position 2 has weight `(-2)^2 = 4`
|
||||
- Position 3 has weight `(-2)^3 = -8`
|
||||
|
||||
When the sum at a position is `2`, we need to carry. But since the next position has weight `-2`, carrying `1` there adds `-2` to our total. To compensate for the `2` we're removing from the current position, we need `2 = 1 × (-2) + ?`, which gives us `-2 + ? = 2`, so `? = 4`. This means we also need to carry to the position *after* that. Similarly, if the sum is `3`, we write `1` and handle the same carry logic.
|
||||
|
||||
If the sum is `-1` (from a negative carry), we write `1` and carry `1` to the next position, because `-1 = 1 + (-2) × 1`.
|
||||
|
||||
approach: |
|
||||
We solve this using a **Right-to-Left Simulation** approach, similar to grade school addition:
|
||||
|
||||
**Step 1: Initialise variables**
|
||||
|
||||
- `result`: An empty list to store the output bits (in reverse order)
|
||||
- `carry`: Set to `0` — this can be `-1`, `0`, or `1` during processing
|
||||
- `i`, `j`: Pointers starting at the rightmost (least significant) positions of both arrays
|
||||
|
||||
|
||||
|
||||
**Step 2: Process from right to left**
|
||||
|
||||
- While there are digits remaining in either array OR there's a non-zero carry:
|
||||
- Get the current digit from `arr1` (or `0` if exhausted)
|
||||
- Get the current digit from `arr2` (or `0` if exhausted)
|
||||
- Compute `sum = digit1 + digit2 + carry`
|
||||
- Handle the sum based on its value:
|
||||
- If `sum == 0` or `sum == 1`: write `sum`, carry becomes `0`
|
||||
- If `sum == 2`: write `0`, carry becomes `-1` (since `2 = 0 + (-2) × (-1)`)
|
||||
- If `sum == 3`: write `1`, carry becomes `-1` (since `3 = 1 + (-2) × (-1)`)
|
||||
- If `sum == -1`: write `1`, carry becomes `1` (since `-1 = 1 + (-2) × 1`)
|
||||
- Move pointers left
|
||||
|
||||
|
||||
|
||||
**Step 3: Clean up and return**
|
||||
|
||||
- Reverse the result list (we built it backwards)
|
||||
- Remove leading zeros, but keep at least one digit
|
||||
- Return the final array
|
||||
|
||||
|
||||
|
||||
The mathematical basis: `sum = result_bit + (-2) × carry`. Rearranging gives us the carry logic for each case.
|
||||
|
||||
common_pitfalls:
|
||||
- title: Treating It Like Regular Binary
|
||||
description: |
|
||||
In regular binary (base 2), when you have a carry of `1` and it propagates, you handle it the same way at each position. But in negabinary, the alternating signs mean the carry behaves differently.
|
||||
|
||||
For example, if `sum = 2`:
|
||||
- In base 2: write `0`, carry `1`
|
||||
- In base -2: write `0`, carry `-1`
|
||||
|
||||
The negative carry compensates for the fact that the next position has a negative weight.
|
||||
wrong_approach: "Using standard binary carry logic"
|
||||
correct_approach: "Use negabinary-specific carry rules"
|
||||
|
||||
- title: Forgetting the -1 Carry Case
|
||||
description: |
|
||||
When a previous carry was `-1` and the current digits are both `0`, the sum becomes `-1`. This is a valid scenario that requires writing `1` and carrying `1` to the next position.
|
||||
|
||||
If you only handle sums of `0`, `1`, `2`, `3`, your algorithm will fail on certain inputs.
|
||||
wrong_approach: "Only handling sums 0-3"
|
||||
correct_approach: "Handle sum = -1 by writing 1 with carry 1"
|
||||
|
||||
- title: Not Removing Leading Zeros Properly
|
||||
description: |
|
||||
The result might have leading zeros after the addition is complete. For instance, adding `[1,0]` and `[1,0]` in base -2 gives a result that might initially be `[0,1,1,0]` before cleanup.
|
||||
|
||||
However, you must keep at least one digit — if the result is all zeros, return `[0]`, not an empty array.
|
||||
wrong_approach: "Stripping all zeros"
|
||||
correct_approach: "Strip leading zeros but keep [0] for zero result"
|
||||
|
||||
key_takeaways:
|
||||
- "**Negabinary carry rules**: Unlike standard binary, carries in base -2 can be negative and require different handling for sums of 2, 3, and -1"
|
||||
- "**Formula-based approach**: Use `sum = result_bit + (-2) × carry` to derive the correct bit and carry values mathematically"
|
||||
- "**Generalises to any base**: This simulation pattern works for addition in any positional numeral system — just adjust the carry logic for the base"
|
||||
- "**Edge cases matter**: Handle the `-1` sum case and ensure proper leading zero removal"
|
||||
|
||||
time_complexity: "O(max(n, m)). We process each digit of both arrays exactly once, where `n` and `m` are the lengths of `arr1` and `arr2`."
|
||||
space_complexity: "O(max(n, m)). The result array can be at most a few digits longer than the longer input."
|
||||
|
||||
solutions:
|
||||
- approach_name: Simulation with Carry
|
||||
is_optimal: true
|
||||
code: |
|
||||
def add_negabinary(arr1: list[int], arr2: list[int]) -> list[int]:
|
||||
result = []
|
||||
carry = 0
|
||||
i, j = len(arr1) - 1, len(arr2) - 1
|
||||
|
||||
# Process digits from right to left
|
||||
while i >= 0 or j >= 0 or carry != 0:
|
||||
# Get current digits (0 if index out of bounds)
|
||||
digit1 = arr1[i] if i >= 0 else 0
|
||||
digit2 = arr2[j] if j >= 0 else 0
|
||||
|
||||
# Calculate sum including carry
|
||||
total = digit1 + digit2 + carry
|
||||
|
||||
# Determine result bit and new carry based on sum
|
||||
# Formula: total = result_bit + (-2) * carry
|
||||
if total == 0:
|
||||
result.append(0)
|
||||
carry = 0
|
||||
elif total == 1:
|
||||
result.append(1)
|
||||
carry = 0
|
||||
elif total == 2:
|
||||
# 2 = 0 + (-2) * (-1)
|
||||
result.append(0)
|
||||
carry = -1
|
||||
elif total == 3:
|
||||
# 3 = 1 + (-2) * (-1)
|
||||
result.append(1)
|
||||
carry = -1
|
||||
elif total == -1:
|
||||
# -1 = 1 + (-2) * 1
|
||||
result.append(1)
|
||||
carry = 1
|
||||
|
||||
i -= 1
|
||||
j -= 1
|
||||
|
||||
# Reverse since we built the result backwards
|
||||
result.reverse()
|
||||
|
||||
# Remove leading zeros but keep at least one digit
|
||||
while len(result) > 1 and result[0] == 0:
|
||||
result.pop(0)
|
||||
|
||||
return result
|
||||
explanation: |
|
||||
**Time Complexity:** O(max(n, m)) — Single pass through both arrays.
|
||||
|
||||
**Space Complexity:** O(max(n, m)) — Result array stores the output.
|
||||
|
||||
We simulate the addition process from right to left, handling the unique carry rules of base -2. The key insight is that when the sum is 2 or 3, we carry -1 (not +1), and when the sum is -1, we carry +1.
|
||||
|
||||
- approach_name: Convert to Decimal and Back
|
||||
is_optimal: false
|
||||
code: |
|
||||
def add_negabinary(arr1: list[int], arr2: list[int]) -> list[int]:
|
||||
def to_decimal(arr: list[int]) -> int:
|
||||
"""Convert negabinary array to decimal."""
|
||||
result = 0
|
||||
for i, bit in enumerate(reversed(arr)):
|
||||
if bit == 1:
|
||||
result += (-2) ** i
|
||||
return result
|
||||
|
||||
def to_negabinary(n: int) -> list[int]:
|
||||
"""Convert decimal to negabinary array."""
|
||||
if n == 0:
|
||||
return [0]
|
||||
|
||||
result = []
|
||||
while n != 0:
|
||||
# Get remainder when dividing by -2
|
||||
remainder = n % (-2)
|
||||
n //= -2
|
||||
|
||||
# Adjust for negative remainder
|
||||
if remainder < 0:
|
||||
remainder += 2
|
||||
n += 1
|
||||
|
||||
result.append(remainder)
|
||||
|
||||
return result[::-1]
|
||||
|
||||
# Convert both to decimal, add, convert back
|
||||
decimal_sum = to_decimal(arr1) + to_decimal(arr2)
|
||||
return to_negabinary(decimal_sum)
|
||||
explanation: |
|
||||
**Time Complexity:** O(n + m) — Convert both arrays, add, convert back.
|
||||
|
||||
**Space Complexity:** O(max(n, m)) — For the result array.
|
||||
|
||||
This approach converts both negabinary numbers to decimal, performs standard addition, then converts back to negabinary. While conceptually simpler, it requires understanding the conversion algorithms and may have issues with very large numbers in languages without arbitrary precision integers.
|
||||
Reference in New Issue
Block a user