1403. Minimum Subsequence in Non-Increasing Order
Problem Description
You are given an array nums
of integers. Your task is to find a subsequence from this array such that the sum of elements in the subsequence is strictly greater than the sum of all the elements that are not included in the subsequence.
The subsequence you return must satisfy the following conditions:
- It should have the minimum possible size (fewest elements)
- If multiple subsequences have the same minimum size, choose the one with the maximum total sum
- The answer must be returned sorted in non-increasing order (descending order)
A subsequence can be formed by removing some (possibly zero) elements from the original array while maintaining the relative order of remaining elements.
For example, if nums = [4,3,10,9,8]
, you would need to find the smallest subset of numbers whose sum is greater than the sum of the remaining numbers. The total sum is 34, so you need a subsequence with sum > 17. The answer would be [10,9]
since 10+9=19 > 15 (which is 4+3+8).
The problem guarantees that the solution with the given constraints is unique.
Intuition
To find a subsequence whose sum is strictly greater than the sum of remaining elements, we need to think about what makes a subsequence optimal. Since we want the minimum size subsequence, we should be greedy and pick the largest elements first - this way we can reach the required sum threshold with fewer elements.
The key insight is that if the total sum of the array is S
, then we need our subsequence sum to be greater than S/2
. This is because if our subsequence has sum t
, then the remaining elements have sum S - t
. For our subsequence sum to be strictly greater than the remaining sum, we need t > S - t
, which simplifies to t > S/2
.
By sorting the array in descending order and greedily picking elements from largest to smallest, we achieve two goals:
- We minimize the number of elements needed (since larger elements help us reach the threshold faster)
- Among all minimum-size subsequences, we automatically get the one with maximum sum (since we're picking the largest available elements)
For example, if nums = [4,3,10,9,8]
with total sum 34
, we need our subsequence sum to exceed 17
. After sorting in descending order [10,9,8,4,3]
, we pick:
- First
10
: sum =10
(not >17
) - Then
9
: sum =19
(now19 > 17
, so we stop)
This greedy approach guarantees we find the optimal solution because once we exceed half the total sum with the largest possible elements, adding any more elements would only increase the size unnecessarily.
Solution Approach
The implementation follows a greedy approach with sorting:
-
Calculate the total sum: First, we compute the sum of all elements in the array using
sum(nums)
and store it in variables
. This represents the total sum we need to split. -
Sort in descending order: We sort the array in descending order using
sorted(nums, reverse=True)
. This ensures we process elements from largest to smallest. -
Greedy selection: We iterate through the sorted array and maintain:
ans
: The result array that stores our subsequencet
: Running sum of elements in our subsequence
-
Check the stopping condition: For each element
x
in the sorted array:- Add
x
to the running sum:t += x
- Append
x
to our answer array:ans.append(x)
- Check if
t > s - t
(equivalently, if our subsequence sum is greater than the remaining sum) - If the condition is met, we break and return the answer
- Add
The algorithm works because:
- By taking elements in descending order, we minimize the number of elements needed
- The moment our subsequence sum exceeds half of the total sum (
t > s - t
is equivalent tot > s/2
), we have found the minimum size subsequence - Since we picked the largest elements first, this subsequence also has the maximum possible sum among all minimum-size subsequences
- The result is already in non-increasing order due to our sorting approach
Time Complexity: O(n log n)
due to sorting, where n
is the length of the input array.
Space Complexity: O(n)
for storing the sorted array and the answer 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 walk through the solution with nums = [6, 7, 7, 9, 3, 2]
:
Step 1: Calculate total sum
- Total sum
s = 6 + 7 + 7 + 9 + 3 + 2 = 34
- We need our subsequence sum to be > 34/2 = 17
Step 2: Sort in descending order
- Sorted array:
[9, 7, 7, 6, 3, 2]
Step 3: Greedy selection
- Initialize:
ans = []
,t = 0
Iteration 1:
- Pick element
9
- Update:
t = 0 + 9 = 9
,ans = [9]
- Check: Is
9 > 34 - 9
? Is9 > 25
? No, continue
Iteration 2:
- Pick element
7
- Update:
t = 9 + 7 = 16
,ans = [9, 7]
- Check: Is
16 > 34 - 16
? Is16 > 18
? No, continue
Iteration 3:
- Pick element
7
- Update:
t = 16 + 7 = 23
,ans = [9, 7, 7]
- Check: Is
23 > 34 - 23
? Is23 > 11
? Yes, stop!
Step 4: Return result
- Return
[9, 7, 7]
(already in non-increasing order)
Verification:
- Subsequence sum: 9 + 7 + 7 = 23
- Remaining elements sum: 6 + 3 + 2 = 11
- Indeed, 23 > 11 โ
- This is the minimum size (3 elements) needed to exceed half the total sum
Solution Implementation
1class Solution:
2 def minSubsequence(self, nums: List[int]) -> List[int]:
3 """
4 Find the minimum subsequence in non-increasing order with sum greater than
5 the sum of remaining elements.
6
7 Args:
8 nums: List of integers
9
10 Returns:
11 List of integers forming the minimum subsequence in non-increasing order
12 """
13 # Initialize result list to store the subsequence
14 result = []
15
16 # Calculate total sum of all elements
17 total_sum = sum(nums)
18
19 # Initialize sum of selected elements
20 selected_sum = 0
21
22 # Sort array in descending order to greedily pick largest elements first
23 sorted_nums = sorted(nums, reverse=True)
24
25 # Iterate through sorted array and keep adding elements to subsequence
26 for num in sorted_nums:
27 # Add current element to selected sum
28 selected_sum += num
29
30 # Add current element to result subsequence
31 result.append(num)
32
33 # Check if selected sum is greater than remaining sum
34 # remaining_sum = total_sum - selected_sum
35 if selected_sum > total_sum - selected_sum:
36 break
37
38 return result
39
1class Solution {
2 public List<Integer> minSubsequence(int[] nums) {
3 // Sort the array in ascending order
4 Arrays.sort(nums);
5
6 // Initialize result list to store the subsequence
7 List<Integer> result = new ArrayList<>();
8
9 // Calculate the total sum of all elements
10 int totalSum = Arrays.stream(nums).sum();
11
12 // Track the sum of elements in our subsequence
13 int subsequenceSum = 0;
14
15 // Iterate from largest to smallest element (greedy approach)
16 for (int i = nums.length - 1; i >= 0; i--) {
17 // Add current element to subsequence
18 subsequenceSum += nums[i];
19 result.add(nums[i]);
20
21 // Check if subsequence sum is greater than remaining sum
22 // subsequenceSum > (totalSum - subsequenceSum) simplifies to subsequenceSum > totalSum/2
23 if (subsequenceSum > totalSum - subsequenceSum) {
24 break;
25 }
26 }
27
28 return result;
29 }
30}
31
1class Solution {
2public:
3 vector<int> minSubsequence(vector<int>& nums) {
4 // Sort the array in descending order to greedily pick largest elements first
5 sort(nums.rbegin(), nums.rend());
6
7 // Calculate the total sum of all elements
8 int totalSum = accumulate(nums.begin(), nums.end(), 0);
9
10 // Track the sum of elements in our subsequence
11 int subsequenceSum = 0;
12
13 // Store the result subsequence
14 vector<int> result;
15
16 // Greedily add elements from largest to smallest until subsequence sum > remaining sum
17 for (int num : nums) {
18 subsequenceSum += num;
19 result.push_back(num);
20
21 // Check if subsequence sum is greater than the sum of remaining elements
22 // subsequenceSum > (totalSum - subsequenceSum) is equivalent to subsequenceSum > totalSum/2
23 if (subsequenceSum > totalSum - subsequenceSum) {
24 break;
25 }
26 }
27
28 return result;
29 }
30};
31
1/**
2 * Finds the minimum subsequence in non-increasing order with sum greater than
3 * the sum of the remaining elements
4 * @param nums - Array of integers to process
5 * @returns Minimum subsequence with maximum sum in non-increasing order
6 */
7function minSubsequence(nums: number[]): number[] {
8 // Sort the array in descending order (largest to smallest)
9 nums.sort((a: number, b: number) => b - a);
10
11 // Calculate the total sum of all elements
12 const totalSum: number = nums.reduce((accumulator: number, current: number) => accumulator + current, 0);
13
14 // Track the sum of the subsequence we're building
15 let subsequenceSum: number = 0;
16
17 // Iterate through sorted array, adding elements to subsequence
18 for (let index: number = 0; ; index++) {
19 // Add current element to subsequence sum
20 subsequenceSum += nums[index];
21
22 // Check if subsequence sum is greater than remaining elements sum
23 // If true, we've found our minimum subsequence
24 if (subsequenceSum > totalSum - subsequenceSum) {
25 // Return the subsequence from start to current index (inclusive)
26 return nums.slice(0, index + 1);
27 }
28 }
29}
30
Time and Space Complexity
Time Complexity: O(n ร log n)
The dominant operation in this algorithm is the sorting of the array nums
using sorted(nums, reverse=True)
, which takes O(n ร log n)
time where n
is the length of the input array. The subsequent operations include:
- Computing the sum of all elements:
O(n)
- Iterating through the sorted array once:
O(n)
Since O(n ร log n)
dominates O(n)
, the overall time complexity is O(n ร log n)
.
Space Complexity: O(log n)
The space complexity analysis considers:
- The
ans
list can grow up toO(n)
in the worst case (when we need all elements) - However, the dominant space consideration for complexity analysis is the auxiliary space used by the sorting algorithm
- Python's
sorted()
function uses Timsort, which requiresO(log n)
space for its recursive stack in the worst case - The reference answer specifically states
O(log n)
, which aligns with the auxiliary space used by the sorting operation
Therefore, the space complexity is O(log n)
, where n
is the length of the array nums
.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Integer Overflow in Sum Calculation
When dealing with large arrays or large integer values, calculating the total sum might cause integer overflow in some programming languages (though Python handles arbitrary precision integers automatically).
Pitfall Example:
# In languages with fixed integer sizes, this could overflow
total_sum = sum(nums) # If nums contains very large values
Solution: In Python, this isn't an issue, but in other languages, use appropriate data types:
# For other languages, ensure using long/int64 types # Or check for potential overflow before operations
2. Incorrect Stopping Condition
A common mistake is using the wrong comparison for determining when to stop adding elements to the subsequence.
Pitfall Example:
# Wrong: comparing with total_sum instead of remaining sum if selected_sum > total_sum: # This would require sum > total, which is impossible break # Wrong: using >= instead of > if selected_sum >= total_sum - selected_sum: # "strictly greater" requirement break
Solution:
# Correct: ensure STRICTLY greater than remaining sum if selected_sum > total_sum - selected_sum: break # Or equivalently: if selected_sum * 2 > total_sum: break
3. Forgetting to Sort or Sorting in Wrong Order
The greedy approach only works when we select elements from largest to smallest.
Pitfall Example:
# Wrong: sorting in ascending order
sorted_nums = sorted(nums) # Would need more elements than necessary
# Wrong: not sorting at all
for num in nums: # Original order might not give minimum size
# ...
Solution:
# Correct: always sort in descending order
sorted_nums = sorted(nums, reverse=True)
4. Modifying Original Array
Some might try to sort the original array in-place, which could be problematic if the original array needs to be preserved.
Pitfall Example:
nums.sort(reverse=True) # Modifies the original array for num in nums: # ...
Solution:
# Create a new sorted array instead
sorted_nums = sorted(nums, reverse=True)
# Original nums array remains unchanged
5. Edge Case: Single Element Array
When the array has only one element, the subsequence must contain that element (sum of empty set is 0).
Pitfall Example:
# Might not handle nums = [1] correctly if logic is flawed
if len(nums) == 0: # But what about len(nums) == 1?
return []
Solution: The current implementation handles this correctly since:
- For
nums = [1]
, total_sum = 1, selected_sum becomes 1 - Check: 1 > 0 (true), so returns [1]
Problem: Given a list of tasks and a list of requirements, compute a sequence of tasks that can be performed, such that we complete every task once while satisfying all the requirements.
Which of the following method should we use to solve this problem?
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!