Facebook Pixel

1846. Maximum Element After Decreasing and Rearranging

Problem Description

You have an array of positive integers arr that you need to modify to meet specific conditions. Your goal is to find the maximum possible value that can exist in the array after applying allowed operations.

The array must satisfy two conditions:

  1. The first element must be equal to 1
  2. The absolute difference between any two adjacent elements must be at most 1 (i.e., abs(arr[i] - arr[i-1]) <= 1)

You can perform two types of operations any number of times:

  1. Decrease operation: Reduce any element's value to a smaller positive integer
  2. Rearrange operation: Reorder the elements in any way you want

The strategy to maximize the final array's largest element involves:

  • Sorting the array first to arrange elements in ascending order
  • Setting the first element to 1 (as required)
  • For each subsequent element, ensuring it differs from the previous element by at most 1
  • If an element is too large (difference > 1), reducing it to be exactly 1 more than the previous element

For example, if you have arr = [2, 2, 1, 2, 1]:

  • After sorting: [1, 1, 2, 2, 2]
  • First element stays/becomes 1: [1, 1, 2, 2, 2]
  • Second element can be at most 2 (1+1), but it's 1, so keep it: [1, 1, 2, 2, 2]
  • Third element can be at most 2 (1+1), it's 2, so keep it: [1, 1, 2, 2, 2]
  • Fourth element can be at most 3 (2+1), it's 2, so keep it: [1, 1, 2, 2, 2]
  • Fifth element can be at most 3 (2+1), it's 2, so keep it: [1, 1, 2, 2, 2]
  • Maximum value is 2

The problem asks you to return this maximum possible value after all operations.

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

Intuition

To maximize the largest element in the final array, we need to build a sequence that grows as quickly as possible while respecting the constraint that adjacent elements can differ by at most 1.

Think of building a staircase where each step can be at most 1 unit higher than the previous one. Since we must start at height 1 (first element = 1), the best we can do is create a sequence like [1, 2, 3, 4, ...] to reach the maximum height as quickly as possible.

The key insight is that sorting the array first gives us the best chance to utilize all available elements effectively. Why? Because:

  • Smaller elements can be placed earlier in the sequence without "wasting" their potential
  • Larger elements can be preserved (or minimally reduced) for later positions where higher values are allowed

Consider what happens after sorting:

  • We have elements in ascending order: [aโ‚, aโ‚‚, aโ‚ƒ, ..., aโ‚™]
  • We set aโ‚ = 1 (required constraint)
  • For each subsequent element aแตข, we ask: "What's the maximum value it can have?"
  • The answer is: at most aแตขโ‚‹โ‚ + 1 (due to the adjacent difference constraint)
  • If aแตข is already smaller than or equal to aแตขโ‚‹โ‚ + 1, we keep it as is
  • If aแตข is larger, we reduce it to aแตขโ‚‹โ‚ + 1

This greedy approach works because:

  1. We never need to decrease an element more than necessary
  2. By processing elements in sorted order, we ensure that each element contributes maximally to building up toward larger values
  3. The final maximum will be the largest value we can reach given the number of elements and their initial values

Essentially, we're building the tallest possible "staircase" where the height is limited either by the constraint (can only go up by 1 each step) or by the original values in the array (can't increase values, only decrease them).

Learn more about Greedy and Sorting patterns.

Solution Approach

The solution implements a sorting + greedy algorithm approach in three main steps:

Step 1: Sort the array

arr.sort()

We sort the array in ascending order to arrange elements from smallest to largest. This allows us to build our sequence optimally, using smaller values first.

Step 2: Set the first element to 1

arr[0] = 1

This satisfies the first constraint that the first element must be 1.

Step 3: Process remaining elements greedily

for i in range(1, len(arr)):
    d = max(0, arr[i] - arr[i - 1] - 1)
    arr[i] -= d

For each element starting from index 1:

  • Calculate the difference d between the current element and what's allowed (previous element + 1)
  • If arr[i] - arr[i-1] <= 1, then d = 0 (no reduction needed)
  • If arr[i] - arr[i-1] > 1, then d = arr[i] - arr[i-1] - 1 (amount to reduce)
  • Reduce the current element by d to ensure it's at most arr[i-1] + 1

Step 4: Return the maximum

return max(arr)

After processing all elements, the maximum value in the array is our answer.

Example walkthrough: For arr = [100, 1, 1000]:

  1. After sorting: [1, 100, 1000]
  2. Set first element: [1, 100, 1000]
  3. Process index 1: 100 - 1 = 99 > 1, so reduce by 98: [1, 2, 1000]
  4. Process index 2: 1000 - 2 = 998 > 1, so reduce by 997: [1, 2, 3]
  5. Return maximum: 3

The time complexity is O(n log n) due to sorting, and space complexity is O(1) as we modify the array in-place.

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 arr = [3, 5, 2, 1]:

Step 1: Sort the array

  • Original: [3, 5, 2, 1]
  • After sorting: [1, 2, 3, 5]

Step 2: Set first element to 1

  • Array becomes: [1, 2, 3, 5] (already 1, no change needed)

Step 3: Process each element greedily

Processing index 1 (value = 2):

  • Previous element: arr[0] = 1
  • Maximum allowed: 1 + 1 = 2
  • Current value: 2
  • Difference to reduce: max(0, 2 - 1 - 1) = 0
  • No reduction needed: [1, 2, 3, 5]

Processing index 2 (value = 3):

  • Previous element: arr[1] = 2
  • Maximum allowed: 2 + 1 = 3
  • Current value: 3
  • Difference to reduce: max(0, 3 - 2 - 1) = 0
  • No reduction needed: [1, 2, 3, 5]

Processing index 3 (value = 5):

  • Previous element: arr[2] = 3
  • Maximum allowed: 3 + 1 = 4
  • Current value: 5
  • Difference to reduce: max(0, 5 - 3 - 1) = 1
  • Reduce by 1: 5 - 1 = 4
  • Array becomes: [1, 2, 3, 4]

Step 4: Return the maximum

  • Final array: [1, 2, 3, 4]
  • Maximum value: 4

The algorithm builds an optimal "staircase" where each element is as large as possible while maintaining the constraint that adjacent elements differ by at most 1.

Solution Implementation

1class Solution:
2    def maximumElementAfterDecrementingAndRearranging(self, arr: List[int]) -> int:
3        # Sort the array in ascending order
4        arr.sort()
5      
6        # First element must be 1 according to problem constraints
7        arr[0] = 1
8      
9        # Iterate through the array starting from the second element
10        for i in range(1, len(arr)):
11            # Calculate the difference needed to maintain the constraint
12            # that adjacent elements differ by at most 1
13            difference = max(0, arr[i] - arr[i - 1] - 1)
14          
15            # Decrease current element to satisfy the constraint
16            arr[i] -= difference
17      
18        # Return the maximum element in the modified array
19        return max(arr)
20
1class Solution {
2    public int maximumElementAfterDecrementingAndRearranging(int[] arr) {
3        // Sort the array in ascending order
4        Arrays.sort(arr);
5      
6        // First element must be 1 according to problem constraints
7        arr[0] = 1;
8      
9        // Initialize the maximum element found so far
10        int maxElement = 1;
11      
12        // Iterate through the array starting from the second element
13        for (int i = 1; i < arr.length; ++i) {
14            // Calculate the difference between current and previous element minus 1
15            // This ensures the absolute difference between adjacent elements is at most 1
16            int excessDifference = Math.max(0, arr[i] - arr[i - 1] - 1);
17          
18            // Decrease current element to maintain the constraint
19            // After this operation, arr[i] - arr[i-1] <= 1
20            arr[i] -= excessDifference;
21          
22            // Update the maximum element found
23            maxElement = Math.max(maxElement, arr[i]);
24        }
25      
26        // Return the maximum element in the modified array
27        return maxElement;
28    }
29}
30
1class Solution {
2public:
3    int maximumElementAfterDecrementingAndRearranging(vector<int>& arr) {
4        // Sort the array in ascending order to optimize the arrangement
5        sort(arr.begin(), arr.end());
6      
7        // First element must be 1 according to the problem constraints
8        arr[0] = 1;
9      
10        // Initialize the maximum element tracker
11        int maxElement = 1;
12      
13        // Process each element starting from index 1
14        for (int i = 1; i < arr.size(); ++i) {
15            // Calculate how much we need to decrease the current element
16            // to maintain the constraint: abs(arr[i] - arr[i-1]) <= 1
17            // The difference should not exceed 1, so arr[i] can be at most arr[i-1] + 1
18            int decrementAmount = max(0, arr[i] - arr[i - 1] - 1);
19          
20            // Apply the decrement to ensure the constraint is satisfied
21            arr[i] -= decrementAmount;
22          
23            // Update the maximum element found so far
24            maxElement = max(maxElement, arr[i]);
25        }
26      
27        // Return the maximum element in the rearranged array
28        return maxElement;
29    }
30};
31
1/**
2 * Finds the maximum possible value of an element in the array after applying operations:
3 * 1. The first element must be 1
4 * 2. The absolute difference between any two adjacent elements must be at most 1
5 * 3. Elements can only be decreased or remain unchanged
6 * 
7 * @param arr - The input array of positive integers
8 * @returns The maximum element value after operations
9 */
10function maximumElementAfterDecrementingAndRearranging(arr: number[]): number {
11    // Sort the array in ascending order to optimize element arrangement
12    arr.sort((a: number, b: number) => a - b);
13  
14    // First element must be 1 according to the problem constraints
15    arr[0] = 1;
16  
17    // Initialize the maximum element value
18    let maxElement: number = 1;
19  
20    // Iterate through the array starting from the second element
21    for (let i: number = 1; i < arr.length; ++i) {
22        // Calculate how much we need to decrease the current element
23        // to maintain the constraint that adjacent difference is at most 1
24        const decrementAmount: number = Math.max(0, arr[i] - arr[i - 1] - 1);
25      
26        // Decrease the current element to satisfy the constraint
27        arr[i] -= decrementAmount;
28      
29        // Update the maximum element value found so far
30        maxElement = Math.max(maxElement, arr[i]);
31    }
32  
33    return maxElement;
34}
35

Time and Space Complexity

Time Complexity: O(n ร— log n)

The time complexity is dominated by the sorting operation arr.sort(), which takes O(n ร— log n) time where n is the length of the array. After sorting, there is a single loop that iterates through the array once with O(1) operations inside the loop, contributing O(n) time. The max(arr) operation at the end also takes O(n) time. Therefore, the overall time complexity is O(n ร— log n) + O(n) + O(n) = O(n ร— log n).

Space Complexity: O(log n)

The space complexity comes from the sorting algorithm. Python's built-in sort() method uses Timsort, which requires O(log n) space for the recursion stack in the worst case. The code modifies the array in-place and only uses a constant amount of extra variables (i and d), so no additional space proportional to the input size is needed beyond the sorting overhead. Therefore, the space complexity is O(log n).

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

Common Pitfalls

Pitfall 1: Unnecessary Final Maximum Search

Issue: The code calls max(arr) at the end to find the maximum element, which adds an unnecessary O(n) operation. Since we're processing elements in sorted order and each element can only be at most 1 greater than the previous element, the last element will always be the maximum.

Problematic code:

return max(arr)  # Unnecessary O(n) scan

Solution:

return arr[-1]  # The last element is guaranteed to be the maximum

Pitfall 2: Modifying Input Array

Issue: The solution modifies the input array directly, which can be problematic if the original array needs to be preserved for other operations or if the interviewer expects a non-destructive solution.

Better approach:

def maximumElementAfterDecrementingAndRearranging(self, arr: List[int]) -> int:
    # Create a copy to avoid modifying the original
    arr_copy = sorted(arr)
    arr_copy[0] = 1
  
    for i in range(1, len(arr_copy)):
        arr_copy[i] = min(arr_copy[i], arr_copy[i-1] + 1)
  
    return arr_copy[-1]

Pitfall 3: Overthinking the Difference Calculation

Issue: The calculation difference = max(0, arr[i] - arr[i - 1] - 1) followed by arr[i] -= difference can be simplified. This two-step process is harder to understand than necessary.

Cleaner approach:

for i in range(1, len(arr)):
    # Directly set to the minimum of current value or maximum allowed value
    arr[i] = min(arr[i], arr[i-1] + 1)

This is more intuitive: each element becomes either its current value (if valid) or the maximum allowed value (previous + 1).

Pitfall 4: Not Recognizing the Pattern

Issue: After understanding the algorithm, developers might not realize that the final maximum is simply min(n, arr[-1]) where n is the array length, since we can increase by at most 1 at each step starting from 1.

Optimized solution without modifying array:

def maximumElementAfterDecrementingAndRearranging(self, arr: List[int]) -> int:
    arr.sort()
    result = 1
  
    for i in range(1, len(arr)):
        result = min(result + 1, arr[i])
  
    return result

This approach uses O(1) extra space (excluding sorting) and doesn't modify the sorted array beyond the initial sort.

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