Table of Contents#
- Introduction
- Problem Statement
- Intuition and Insight
- Algorithm Selection: Frequency Array and Moving Pointer
- Why Avoid Explicit Sorting?
- Detailed O(n) Algorithm
- Handling Leading Zeros
- Time Complexity Analysis
- Space Complexity Analysis
- Example Walkthrough
- Common Practices and Best Practices
- Edge Cases
- Implementation in Python
- References
Problem Statement#
Given an array arr of length n where each element is a digit (0–9), construct two numbers A and B by partitioning the digits arbitrarily such that:
- Every digit in
arris used exactly once. - The sum
A + Bis minimized.
Constraints:
- Must run in O(n) time.
- Must use O(n) extra space (for storing digits in lists).
Intuition and Insight#
The minimal sum is achieved by:
- Minimizing Most Significant Digits (MSD): Assign the smallest available digits to the MSD positions of the two numbers to reduce their overall magnitude.
- Balanced Digit Distribution: Alternate digit assignment between the two numbers to balance their lengths. This prevents one number from becoming disproportionately large.
- Greedy Assignment: Process digits in sorted order (0–9) to ensure the smallest digits are used first, optimizing the sum.
Key observation: Any explicit sorting would be O(n log n), which is inefficient for constraints. Instead, we use a frequency array to simulate sorted order in O(n).
Algorithm Selection: Frequency Array and Moving Pointer#
- Frequency Array: Count occurrences of each digit (0–9) in O(n) time.
- Moving Pointer (
min_avail): Track the smallest digit available at any point, initialized to 0. This avoids rebuilding a sorted array. - Alternate Assignment: Assign each digit from
min_availto eitherAorBin alternating turns. After assigning, decrement its frequency and advancemin_availif exhausted. - Digit to Number Conversion: Use strings to avoid expensive arithmetic during digit assembly, ensuring O(n) conversion to integers.
Why Avoid Explicit Sorting?#
- Explicit sorting (e.g., comparison-based sort) is O(n log n) for an array of
ndigits. - Counting sort could work for digits (fixed range 0–9), but requires O(n) space for the output array and adds memory allocation overhead for large
n. - Solution: Simulate sorted traversal using a frequency array and moving
min_availpointer:- Total steps for pointer advancement: At most 10 (digits 0–9) over the entire loop (amortized O(1)).
Detailed O(n) Algorithm#
- Frequency Counting:
- Initialize an array
freqof size 10 (for digits 0–9). - For each digit in
arr, incrementfreq[d].
- Initialize an array
- Digit Assignment:
- Initialize empty lists
listAandlistBfor digits of the two numbers. min_avail = 0: Tracks the smallest available digit.turn = 0: Alternates assignment (0 forA, 1 forB).- For each of the
ndigits:- Advance
min_availuntil a digit withfreq[min_avail] > 0is found. - Assign digit
d = min_availtolistA(ifturn==0) orlistB(ifturn==1). - Decrement
freq[d]and toggleturn.
- Advance
- Initialize empty lists
- Number Conversion:
- Convert
listAandlistBto integers via string conversion to avoid expensive arithmetic.
- Convert
Handling Leading Zeros#
- Issue: Digits assigned to MSD positions could be zero, but integers ignore leading zeros.
- Solution: Assign
0toAorBeven if it’s the first digit. During conversion,int('0...0')evaluates to0. Example:listA = [0,0,2]→int("002") → 2.
- This is valid since
002simplifies to2.
Time Complexity Analysis#
- Frequency Counting: O(n).
- Digit Assignment:
min_availmoves forward (0→9) at most 10 times (amortized O(1) per digit).- Total: O(n).
- String Conversion:
joinandintconversion for lists of sizekandmis O(k) + O(m) = O(n).
- Total: O(n).
Space Complexity Analysis#
- Frequency Array: O(1) (fixed-size 10).
- Digit Lists: O(n) (storage for
listAandlistB). - Strings: O(n) for strings, freed after conversion.
- Total Auxiliary Space: O(n).
Example Walkthrough#
Example 1: [1, 2, 3, 4]#
- Frequency Array:
[0,1,1,1,1,0,0,0,0,0] - Assign Turns:
- Turn 0:
d=1→A=[1] - Turn 1:
d=2→B=[2] - Turn 0:
d=3→A=[1,3] - Turn 1:
d=4→B=[2,4]
- Turn 0:
- Numbers:
A=13,B=24→ Sum = 37.
Example 2: [0, 0, 0, 1, 2]#
- Frequency Array:
[3,1,1,0,0,0,0,0,0,0] - Assign Turns:
- Turn 0:
d=0→A=[0] - Turn 1:
d=0→B=[0] - Turn 0:
d=0→A=[0,0] - Turn 1:
d=1→B=[0,1] - Turn 0:
d=2→A=[0,0,2]
- Turn 0:
- Numbers:
A = int("002") = 2,B = int("01") = 1→ Sum = 3.
Common Practices and Best Practices#
- Frequency Array Initialization: Use fixed-size arrays (size=10) for digit counting.
- Pointer Optimization: Use
min_availto skip empty digits efficiently. - Avoid Big Integers: Use string conversion to prevent O(n²) arithmetic overhead.
- Handling Invalid Digits: Skip or validate inputs to avoid undefined behavior.
- Edge Cases:
- All Zeros: Result is
0(e.g.,A=0,B=0). - Single Number: One number will be all digits, the other 0.
- All Zeros: Result is
Edge Cases#
- Empty Array: Return 0.
- Single Digit: Assign to
A,B=0. - All Zeros:
A=0,B=0→ Sum=0. - Invalid Digits: Skip non-0–9 inputs.
Implementation in Python#
def min_sum(arr):
n = len(arr)
if n == 0:
return 0
freq = [0] * 10
for d in arr:
if 0 <= d <= 9:
freq[d] += 1
listA = []
listB = []
turn = 0
min_avail = 0
for _ in range(n):
# Advance min_avail to next available digit
while min_avail < 10 and freq[min_avail] == 0:
min_avail += 1
if min_avail == 10:
break # Safety break
d = min_avail
if turn == 0:
listA.append(d)
else:
listB.append(d)
freq[d] -= 1
turn ^= 1 # Toggle between 0 and 1
# Convert lists to integers via strings
a_val = 0
if listA:
a_str = ''.join(str(d) for d in listA)
a_val = int(a_str)
b_val = 0
if listB:
b_str = ''.join(str(d) for d in listB)
b_val = int(b_str)
return a_val + b_val
# Example usage:
print(min_sum([1, 2, 3, 4])) # Output: 37
print(min_sum([0, 0, 0, 1, 2])) # Output: 3
print(min_sum([])) # Output: 0References#
- GeeksforGeeks: Minimum Sum of Two Numbers Formed from Digits of an Array
- Counting Sort: Wikipedia
- Amortized Complexity: Amortized Analysis