561. Array Partition
Problem Description
You are given an integer array nums
containing 2n
integers. Your task is to group these integers into n
pairs: (aโ, bโ), (aโ, bโ), ..., (aโ, bโ)
.
For each pair (aแตข, bแตข)
, you need to take the minimum value min(aแตข, bแตข)
. Your goal is to maximize the sum of all these minimum values.
Return the maximum possible sum.
For example, if you have nums = [1, 4, 3, 2]
, you need to form 2 pairs. If you pair them as (1, 4)
and (2, 3)
, the sum would be min(1, 4) + min(2, 3) = 1 + 2 = 3
. But if you pair them as (1, 2)
and (3, 4)
, the sum would be min(1, 2) + min(3, 4) = 1 + 3 = 4
, which is larger.
The solution works by sorting the array first. After sorting, pairing adjacent elements gives the optimal result. This is because for each pair, we want the two numbers to be as close as possible in value - this way, when we take the minimum, we're "wasting" the least amount from the larger number. By taking every other element from the sorted array (elements at even indices), we get the sum of all the minimum values from the optimal pairing.
Intuition
To understand why we should pair adjacent elements after sorting, let's think about what happens when we form pairs.
When we pick two numbers to form a pair, we only get to add the smaller one to our sum - the larger one is essentially "wasted". So the key insight is: we want to minimize the waste.
Consider having numbers like [1, 2, 5, 6]
. If we pair (1, 6)
and (2, 5)
, we get min(1, 6) + min(2, 5) = 1 + 2 = 3
. Here, we're wasting 6 - 1 = 5
from the first pair and 5 - 2 = 3
from the second pair, total waste = 8
.
But if we pair (1, 2)
and (5, 6)
, we get min(1, 2) + min(5, 6) = 1 + 5 = 6
. Now we're only wasting 2 - 1 = 1
from the first pair and 6 - 5 = 1
from the second pair, total waste = 2
.
This reveals the pattern: to minimize waste, pair numbers that are close to each other in value. And the closest numbers to each other are adjacent numbers in a sorted array!
Once sorted, we take every alternate element starting from index 0 (i.e., elements at positions 0, 2, 4, ...). These represent the smaller element from each adjacent pair, and their sum gives us the maximum possible result.
Solution Approach
The implementation follows a straightforward greedy approach based on our intuition about pairing adjacent elements after sorting.
Step 1: Sort the array
First, we sort the entire array nums
in ascending order using nums.sort()
. This arranges all elements from smallest to largest, ensuring that adjacent elements have the minimum difference in value.
Step 2: Sum alternate elements
After sorting, we need to sum up the smaller element from each adjacent pair. Since we've arranged the array in ascending order, for any adjacent pair (nums[i], nums[i+1])
, the element at index i
(where i
is even) will always be the minimum.
The Python slice notation nums[::2]
elegantly extracts all elements at even indices (0, 2, 4, ...). This gives us exactly the minimum element from each adjacent pair.
Step 3: Return the sum
Finally, we use Python's built-in sum()
function to calculate the total of all these minimum values and return it.
Time Complexity: O(n log n)
where n
is the length of the array, dominated by the sorting operation.
Space Complexity: O(1)
if we consider the sorting to be in-place (though technically Python's sort uses O(n)
auxiliary space).
The beauty of this solution lies in its simplicity - by recognizing that we need to pair adjacent elements after sorting, the entire problem reduces to just two lines of code: sort and sum alternate elements.
Ready to land your dream job?
Unlock your dream job with a 3-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through the solution with nums = [6, 2, 6, 5, 1, 2]
.
Step 1: Sort the array
After sorting: [1, 2, 2, 5, 6, 6]
Step 2: Form pairs from adjacent elements The pairs would be:
- Pair 1:
(1, 2)
โ minimum = 1 - Pair 2:
(2, 5)
โ minimum = 2 - Pair 3:
(6, 6)
โ minimum = 6
Step 3: Extract elements at even indices
Elements at even indices (0, 2, 4): [1, 2, 6]
These are exactly the minimum values from each pair!
Step 4: Calculate the sum Sum = 1 + 2 + 6 = 9
Let's verify this is optimal by trying a different pairing:
- If we paired
(1, 6)
,(2, 6)
,(2, 5)
: sum = 1 + 2 + 2 = 5 (worse) - If we paired
(1, 5)
,(2, 2)
,(6, 6)
: sum = 1 + 2 + 6 = 9 (same, but notice(2, 2)
is still adjacent in sorted order)
The key insight: by pairing adjacent sorted elements, we minimize the "waste" (difference between paired numbers). In our optimal pairing, we waste only (2-1) + (5-2) + (6-6) = 4 total, whereas other pairings waste much more.
Solution Implementation
1class Solution:
2 def arrayPairSum(self, nums: List[int]) -> int:
3 """
4 Find the maximum sum of min(a_i, b_i) for all pairs in the array.
5
6 Strategy: Sort the array and take every alternate element starting from index 0.
7 This ensures we pair smallest with second smallest, third smallest with fourth smallest, etc.
8 This maximizes the sum since we're always choosing the smaller element from each optimal pair.
9
10 Args:
11 nums: List of integers to form pairs
12
13 Returns:
14 Maximum possible sum of minimum elements from all pairs
15 """
16 # Sort the array in ascending order
17 nums.sort()
18
19 # Sum every alternate element starting from index 0 (elements at even indices)
20 # nums[::2] creates a slice with step 2, giving us elements at indices 0, 2, 4, ...
21 return sum(nums[::2])
22
1class Solution {
2 /**
3 * Finds the maximum sum of min(ai, bi) for all pairs in the array.
4 * The strategy is to sort the array and pair adjacent elements.
5 * This ensures we minimize the "waste" by pairing elements that are close in value.
6 *
7 * @param nums The input array containing 2n integers
8 * @return The maximum sum of minimums from all pairs
9 */
10 public int arrayPairSum(int[] nums) {
11 // Sort the array in ascending order
12 Arrays.sort(nums);
13
14 // Initialize the sum to store the result
15 int sum = 0;
16
17 // Iterate through the sorted array, taking every other element (at even indices)
18 // Since the array is sorted, nums[i] will always be the minimum in the pair (nums[i], nums[i+1])
19 for (int i = 0; i < nums.length; i += 2) {
20 sum += nums[i];
21 }
22
23 return sum;
24 }
25}
26
1class Solution {
2public:
3 int arrayPairSum(vector<int>& nums) {
4 // Sort the array in ascending order
5 // This ensures that pairing adjacent elements minimizes the loss
6 sort(nums.begin(), nums.end());
7
8 // Initialize the sum of minimum values from each pair
9 int sum = 0;
10
11 // Iterate through the sorted array, taking every other element
12 // These represent the minimum values in each optimal pair
13 for (int i = 0; i < nums.size(); i += 2) {
14 sum += nums[i];
15 }
16
17 // Return the maximum possible sum of minimums
18 return sum;
19 }
20};
21
1/**
2 * Given an integer array nums of 2n integers, group these integers into n pairs
3 * such that the sum of min(ai, bi) for all pairs is maximized.
4 *
5 * @param nums - Array of integers with even length
6 * @returns Maximum sum of min values from all pairs
7 */
8function arrayPairSum(nums: number[]): number {
9 // Sort the array in ascending order
10 // The optimal strategy is to pair adjacent elements after sorting
11 nums.sort((a: number, b: number) => a - b);
12
13 // Sum up elements at even indices (0, 2, 4, ...)
14 // These represent the smaller element in each adjacent pair
15 return nums.reduce((accumulator: number, currentValue: number, index: number) => {
16 // Only add elements at even indices to the sum
17 return index % 2 === 0 ? accumulator + currentValue : accumulator;
18 }, 0);
19}
20
Time and Space Complexity
The time complexity is O(n ร log n)
, where n
is the length of the array nums
. This is dominated by the sorting operation nums.sort()
, which uses Timsort algorithm in Python with O(n ร log n)
time complexity in the average and worst cases. The subsequent slicing and summation operation sum(nums[::2])
takes O(n/2)
= O(n)
time, which is absorbed by the larger sorting complexity.
The space complexity is O(log n)
. While the sorting operation in Python's Timsort is an in-place sort that modifies the original array, it still requires O(log n)
auxiliary space for the recursion stack during the merge sort phase. The slicing operation nums[::2]
creates a new list containing half the elements, which would typically require O(n)
space, but since this is passed directly to sum()
and Python can optimize this as a generator expression in some implementations, the dominant space complexity remains O(log n)
from the sorting algorithm's recursion stack.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Attempting to Generate All Possible Pairings
A common mistake is trying to generate all possible pairings and then finding the one with maximum sum. This leads to an exponential time complexity solution that's unnecessarily complex.
Incorrect Approach:
def arrayPairSum(self, nums: List[int]) -> int:
# Wrong: Trying to generate all permutations and pairings
from itertools import permutations
max_sum = float('-inf')
for perm in permutations(nums):
current_sum = 0
for i in range(0, len(perm), 2):
current_sum += min(perm[i], perm[i+1])
max_sum = max(max_sum, current_sum)
return max_sum
Why it's wrong: This has O((2n)!) time complexity and will time out for large inputs.
2. Misunderstanding the Pairing After Sorting
Some might think that after sorting, we should pair the smallest with the largest, second smallest with second largest, etc.
Incorrect Approach:
def arrayPairSum(self, nums: List[int]) -> int:
# Wrong: Pairing smallest with largest
nums.sort()
total = 0
left, right = 0, len(nums) - 1
while left < right:
total += min(nums[left], nums[right]) # This will always be nums[left]
left += 1
right -= 1
return total
Why it's wrong: This approach always picks the smaller half of the sorted array, which gives us the minimum possible sum, not the maximum. When you pair the smallest with the largest, you "waste" the largest values completely.
3. Off-by-One Errors in Manual Iteration
When manually iterating through pairs instead of using slicing, it's easy to make indexing mistakes.
Incorrect Approach:
def arrayPairSum(self, nums: List[int]) -> int:
nums.sort()
total = 0
# Wrong: Should be range(0, len(nums), 2) not range(0, len(nums)-1, 2)
for i in range(0, len(nums)-1, 2):
total += nums[i]
return total
Why it's wrong: If the array has an even length (which it always does in this problem), using len(nums)-1
as the stop value will miss the last pair.
4. Forgetting to Sort In-Place vs Creating New Array
While not incorrect, creating a new sorted array instead of sorting in-place uses unnecessary extra space.
Less Optimal:
def arrayPairSum(self, nums: List[int]) -> int:
# Creates a new sorted list (uses extra O(n) space)
sorted_nums = sorted(nums)
return sum(sorted_nums[::2])
Better Solution:
def arrayPairSum(self, nums: List[int]) -> int:
# Sorts in-place (modifies original array)
nums.sort()
return sum(nums[::2])
The in-place sort is more memory-efficient, though both approaches are correct.
What's the output of running the following function using input 56
?
1KEYBOARD = {
2 '2': 'abc',
3 '3': 'def',
4 '4': 'ghi',
5 '5': 'jkl',
6 '6': 'mno',
7 '7': 'pqrs',
8 '8': 'tuv',
9 '9': 'wxyz',
10}
11
12def letter_combinations_of_phone_number(digits):
13 def dfs(path, res):
14 if len(path) == len(digits):
15 res.append(''.join(path))
16 return
17
18 next_number = digits[len(path)]
19 for letter in KEYBOARD[next_number]:
20 path.append(letter)
21 dfs(path, res)
22 path.pop()
23
24 res = []
25 dfs([], res)
26 return res
27
1private static final Map<Character, char[]> KEYBOARD = Map.of(
2 '2', "abc".toCharArray(),
3 '3', "def".toCharArray(),
4 '4', "ghi".toCharArray(),
5 '5', "jkl".toCharArray(),
6 '6', "mno".toCharArray(),
7 '7', "pqrs".toCharArray(),
8 '8', "tuv".toCharArray(),
9 '9', "wxyz".toCharArray()
10);
11
12public static List<String> letterCombinationsOfPhoneNumber(String digits) {
13 List<String> res = new ArrayList<>();
14 dfs(new StringBuilder(), res, digits.toCharArray());
15 return res;
16}
17
18private static void dfs(StringBuilder path, List<String> res, char[] digits) {
19 if (path.length() == digits.length) {
20 res.add(path.toString());
21 return;
22 }
23 char next_digit = digits[path.length()];
24 for (char letter : KEYBOARD.get(next_digit)) {
25 path.append(letter);
26 dfs(path, res, digits);
27 path.deleteCharAt(path.length() - 1);
28 }
29}
30
1const KEYBOARD = {
2 '2': 'abc',
3 '3': 'def',
4 '4': 'ghi',
5 '5': 'jkl',
6 '6': 'mno',
7 '7': 'pqrs',
8 '8': 'tuv',
9 '9': 'wxyz',
10}
11
12function letter_combinations_of_phone_number(digits) {
13 let res = [];
14 dfs(digits, [], res);
15 return res;
16}
17
18function dfs(digits, path, res) {
19 if (path.length === digits.length) {
20 res.push(path.join(''));
21 return;
22 }
23 let next_number = digits.charAt(path.length);
24 for (let letter of KEYBOARD[next_number]) {
25 path.push(letter);
26 dfs(digits, path, res);
27 path.pop();
28 }
29}
30
Recommended Readings
Greedy Introduction div class responsive iframe iframe src https www youtube com embed WTslqPbj7I title YouTube video player frameborder 0 allow accelerometer autoplay clipboard write encrypted media gyroscope picture in picture web share allowfullscreen iframe div When do we use greedy Greedy algorithms tend to solve optimization problems Typically they will ask you to calculate the max min of some value Commonly you may see this phrased in the problem as max min longest shortest largest smallest etc These keywords can be identified by just scanning
Sorting Summary Comparisons We presented quite a few sorting algorithms and it is essential to know the advantages and disadvantages of each one The basic algorithms are easy to visualize and easy to learn for beginner programmers because of their simplicity As such they will suffice if you don't know any advanced
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
Want a Structured Path to Master System Design Too? Donโt Miss This!