Facebook Pixel

2592. Maximize Greatness of an Array

Problem Description

You have an integer array nums with 0-based indexing. Your task is to rearrange this array into a new permutation called perm.

The greatness of the array is defined as the count of indices i (where 0 <= i < nums.length) for which perm[i] > nums[i]. In other words, greatness measures how many positions have a value in the permuted array that is strictly greater than the value at the same position in the original array.

Your goal is to find a permutation of nums that maximizes this greatness value and return the maximum possible greatness.

For example, if nums = [1, 3, 5, 2, 1, 3, 1], you could rearrange it to perm = [2, 5, 1, 3, 3, 1, 1]. Comparing position by position:

  • Position 0: 2 > 1 βœ“
  • Position 1: 5 > 3 βœ“
  • Position 2: 1 < 5 βœ—
  • Position 3: 3 > 2 βœ“
  • Position 4: 3 > 1 βœ“
  • Position 5: 1 < 3 βœ—
  • Position 6: 1 = 1 βœ—

This gives a greatness of 4 (positions 0, 1, 3, and 4 satisfy the condition).

The solution uses a greedy approach by sorting the array first. It maintains a pointer i starting at index 0. As it iterates through each element x in the sorted array, if x is greater than nums[i], it increments i. The final value of i represents the maximum greatness achievable, as it counts how many elements from the sorted array can be matched with smaller elements to create the perm[i] > nums[i] condition.

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

Intuition

To maximize greatness, we need to pair each element in the original array with a larger element from the same array in the permuted version. The key insight is that we want to use the smallest possible "larger" element for each position to avoid wasting our larger values.

Think of it this way: if we have a value 3 in the original array, we could pair it with 7 or 4 from our available elements. Using 4 instead of 7 is better because we save 7 for potentially pairing with a larger value later.

This naturally leads us to sort the array first. Once sorted, we can use a two-pointer approach where we try to create the most efficient pairings.

Consider the sorted array as two roles:

  • The "original positions" that need to be beaten
  • The "candidates" that we can use to beat them

We start with the smallest element in the "original positions" and try to find the smallest element from our "candidates" that can beat it. If we find one, we move to the next position to beat and the next candidate. If the current candidate can't beat the current position, we skip that candidate and try the next one.

For example, with sorted array [1, 1, 2, 3, 3, 5]:

  • Can we beat 1 (at index 0)? Yes, with 2 β†’ move both pointers
  • Can we beat 1 (at index 1)? Yes, with 3 β†’ move both pointers
  • Can we beat 2 (at index 2)? Yes, with 3 β†’ move both pointers
  • Can we beat 3 (at index 3)? Yes, with 5 β†’ move both pointers
  • No more candidates left

This greedy strategy ensures we maximize the number of positions where perm[i] > nums[i] by always using the smallest sufficient value, preserving larger values for later use.

Learn more about Greedy, Two Pointers and Sorting patterns.

Solution Approach

The implementation follows a greedy algorithm with these steps:

  1. Sort the array: First, we sort nums in ascending order. This allows us to efficiently match smaller elements with their smallest possible "greater" counterparts.

    nums.sort()
  2. Initialize a pointer: We use a single pointer i starting at position 0. This pointer tracks the current position in the sorted array that we're trying to "beat" (find a larger element for).

    i = 0
  3. Traverse and match elements: We iterate through each element x in the sorted array. Each x serves as a potential candidate to beat the element at position i:

    for x in nums:
        i += x > nums[i]

    The clever part here is the expression x > nums[i]:

    • If x > nums[i], the comparison returns True (which equals 1 in Python), so i increments by 1
    • If x <= nums[i], the comparison returns False (which equals 0), so i stays the same

    When we increment i, it means we've successfully found a pairing where the permuted value beats the original value. We then move to the next position to beat.

  4. Return the result: The final value of i represents the maximum number of positions where we achieved perm[i] > nums[i].

Why this works: By processing elements in sorted order, we ensure that:

  • We always try to use the smallest possible element that can beat the current position
  • We never waste larger elements on positions that could be beaten by smaller ones
  • The pointer i only advances when we make a successful pairing, automatically counting our greatness

Time Complexity: O(n log n) due to sorting, where n is the length of the array.

Space Complexity: O(1) if we don't count the space used for sorting (in-place sorting), or O(n) if we consider the sorting space.

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 walk through the solution with nums = [2, 1, 3, 1].

Step 1: Sort the array

  • Original: [2, 1, 3, 1]
  • Sorted: [1, 1, 2, 3]

Step 2: Initialize pointer

  • i = 0 (pointing to the first element we want to "beat")

Step 3: Iterate through sorted array

IterationCurrent xnums[i]Is x > nums[i]?ActionNew i
11nums[0] = 11 > 1? Noi stays same0
21nums[0] = 11 > 1? Noi stays same0
32nums[0] = 12 > 1? Yesi increments1
43nums[1] = 13 > 1? Yesi increments2

Step 4: Return result

  • Final i = 2, so maximum greatness is 2

Understanding the result:

  • We successfully paired:
    • Position 0 (value 1) with element 2 β†’ 2 > 1 βœ“
    • Position 1 (value 1) with element 3 β†’ 3 > 1 βœ“
  • We couldn't beat positions 2 and 3 because we used up our larger elements

This means we can create a permutation like [2, 3, 1, 1] when mapped back to original indices, achieving a greatness of 2 (though the actual permutation construction isn't part of this algorithm - we only calculate the maximum achievable greatness).

Solution Implementation

1class Solution:
2    def maximizeGreatness(self, nums: List[int]) -> int:
3        # Sort the array to enable greedy matching
4        nums.sort()
5      
6        # Initialize pointer for the smaller elements to be upgraded
7        left_pointer = 0
8      
9        # Iterate through each element as a potential larger element
10        for current_value in nums:
11            # If current element can upgrade the element at left_pointer
12            # Move the left_pointer forward (counting one successful upgrade)
13            left_pointer += current_value > nums[left_pointer]
14      
15        # The final position of left_pointer equals the number of successful upgrades
16        return left_pointer
17
1class Solution {
2    public int maximizeGreatness(int[] nums) {
3        // Sort the array in ascending order
4        Arrays.sort(nums);
5      
6        // Initialize pointer for the smaller element in comparison
7        int smallerIndex = 0;
8      
9        // Iterate through each element in the sorted array
10        for (int currentNum : nums) {
11            // If current element is greater than the element at smallerIndex,
12            // we found a valid pair where currentNum > nums[smallerIndex]
13            // This contributes to the greatness count
14            if (currentNum > nums[smallerIndex]) {
15                // Move the smaller pointer forward as we've used this element
16                smallerIndex++;
17            }
18        }
19      
20        // The final position of smallerIndex represents the maximum greatness
21        // (number of indices where permutation value > original value)
22        return smallerIndex;
23    }
24}
25
1class Solution {
2public:
3    int maximizeGreatness(vector<int>& nums) {
4        // Sort the array in ascending order
5        sort(nums.begin(), nums.end());
6      
7        // Use two-pointer technique:
8        // 'greatnessCount' tracks the position of smaller elements
9        // and also counts the number of valid pairs
10        int greatnessCount = 0;
11      
12        // Iterate through each element in the sorted array
13        for (int currentNum : nums) {
14            // If current element is greater than the element at position 'greatnessCount',
15            // we found a valid pair where currentNum can be placed to achieve greatness
16            if (currentNum > nums[greatnessCount]) {
17                greatnessCount++;
18            }
19        }
20      
21        return greatnessCount;
22    }
23};
24
1/**
2 * Finds the maximum greatness of the array.
3 * Greatness is defined as the maximum number of elements that can be made greater
4 * by rearranging the array such that nums2[i] > nums1[i].
5 * 
6 * @param nums - The input array of numbers
7 * @returns The maximum greatness value
8 */
9function maximizeGreatness(nums: number[]): number {
10    // Sort the array in ascending order
11    nums.sort((a: number, b: number) => a - b);
12  
13    // Pointer to track the position in the original sorted array
14    let pointerIndex: number = 0;
15  
16    // Iterate through each element in the sorted array
17    for (const currentValue of nums) {
18        // If current value is greater than the value at pointer position,
19        // we can create a valid pair where currentValue > nums[pointerIndex]
20        if (currentValue > nums[pointerIndex]) {
21            // Move pointer forward as we've found a valid pairing
22            pointerIndex += 1;
23        }
24    }
25  
26    // The pointer index represents the count of valid pairings (maximum greatness)
27    return pointerIndex;
28}
29

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 for loop iterates through the array once with O(n) operations, but this is absorbed by the larger sorting complexity.

The space complexity is O(log n), where n is the length of the array nums. While the sorting is done in-place and doesn't require additional space proportional to the input size, the sorting algorithm itself uses O(log n) space for the recursion stack during the divide-and-conquer process. The remaining operations use only O(1) additional space for the variable i and the loop variable x.

Learn more about how to find time and space complexity quickly.

Common Pitfalls

Pitfall 1: Misunderstanding the Pointer Logic

The Problem: Many developers initially misinterpret what the pointer i represents and try to use two separate pointers or arrays to track matches. They might attempt something like:

def maximizeGreatness(self, nums: List[int]) -> int:
    sorted_nums = sorted(nums)
    original = sorted(nums)  # Unnecessary copy
    count = 0
    used = [False] * len(nums)  # Tracking used elements
  
    for i in range(len(nums)):
        for j in range(len(sorted_nums)):
            if not used[j] and sorted_nums[j] > original[i]:
                count += 1
                used[j] = True
                break
    return count

Why It's Wrong: This approach overcomplicates the solution and doesn't leverage the key insight that we're comparing the same sorted array against itself with an offset.

The Fix: Understand that we're essentially trying to match elements from the sorted array with themselves, shifted by some positions. The single pointer tracks how many successful "shifts" we can make.

Pitfall 2: Confusing In-Place Modification

The Problem: Some might think the code is creating the actual permutation array and get confused when the function only returns a count:

def maximizeGreatness(self, nums: List[int]) -> int:
    nums.sort()
    perm = []  # Trying to build the actual permutation
    i = 0
  
    for x in nums:
        if x > nums[i]:
            perm.append(x)  # Wrong approach
            i += 1
  
    return len(perm)  # This won't give the correct answer

Why It's Wrong: The problem only asks for the maximum greatness value (a count), not the actual permutation. Building the permutation array incorrectly will lead to wrong results.

The Fix: Focus on counting successful pairings rather than constructing the actual permutation. The algorithm works by determining how many elements from the sorted array can be successfully paired with smaller elements.

Pitfall 3: Incorrect Boolean Addition

The Problem: In languages other than Python, or for developers unfamiliar with Python's boolean arithmetic, the line i += x > nums[i] might be confusing or implemented incorrectly:

def maximizeGreatness(self, nums: List[int]) -> int:
    nums.sort()
    i = 0
  
    for x in nums:
        if x > nums[i]:
            i = i + 1  # More verbose but clearer
        # Forgetting the else case or incrementing i always
        i += 1  # Wrong! This would increment twice when condition is true
  
    return i

Why It's Wrong: Adding an extra increment outside the conditional would count elements twice or advance the pointer incorrectly.

The Fix: Use either the concise boolean addition i += x > nums[i] or the explicit conditional:

for x in nums:
    if x > nums[i]:
        i += 1
    # No else needed; i stays the same if condition is false

Pitfall 4: Not Handling Edge Cases

The Problem: Failing to consider arrays where all elements are identical:

nums = [1, 1, 1, 1]

Why It Matters: In this case, no element can be greater than any other, so the maximum greatness should be 0. The algorithm handles this correctly (the pointer never advances), but developers might overthink and add unnecessary special case handling.

The Fix: Trust the algorithm - it naturally handles this case without special logic since x > nums[i] will always be False when all elements are equal.

Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

Which algorithm should you use to find a node that is close to the root of the tree?


Recommended Readings

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

Load More