Maximum Element After Decreasing and Rearranging
MedPrompt
You are given an array of positive integers arr. Perform some operations (possibly none) on arr so that it satisfies these conditions:
- The value of the first element in
arrmust be1. - The absolute difference between any 2 adjacent elements must be less than or equal to
1. In other words,abs(arr[i] - arr[i - 1]) <= 1for eachiwhere1 <= i < arr.length(0-indexed).abs(x)is the absolute value ofx.
There are 2 types of operations that you can perform any number of times:
- Decrease the value of any element of
arrto a smaller positive integer. - Rearrange the elements of
arrto be in any order.
Return the maximum possible value of an element in arr after performing the operations to satisfy the conditions.
Example 1:
arrExample 2:
arrExample 3:
Input: arr = [1,2,3,4,5]
Output: 5
Explanation: The array already satisfies the conditions, and the largest element is 5.
Constraints:
1 <= arr.length <= 1051 <= arr[i] <= 109
Approaches
2 approaches with complexity analysis and trade-offs.
The core idea is that to maximize the final element, we should make the array a non-decreasing sequence. By sorting the input array, we can process the elements in increasing order. This allows us to greedily construct the target array by ensuring each element satisfies the condition arr[i] <= arr[i-1] + 1 while being as large as possible.
Algorithm
- Sort the input array
arrin non-decreasing order. - Set the first element
arr[0]to1to satisfy the first condition. - Iterate through the array from the second element (
i = 1ton-1). - For each element
arr[i], if it violates the conditionarr[i] - arr[i-1] <= 1(i.e.,arr[i] > arr[i-1] + 1), decrease its value toarr[i-1] + 1. This is the minimal change to satisfy the condition while keeping the value as large as possible. - After the loop, the array is valid, and its maximum element is the last one,
arr[n-1]. Return this value.
Walkthrough
This approach leverages the power of rearranging by sorting the array first. Sorting is a key step because it allows us to build the resulting array greedily and optimally.
-
Sort the array: We sort
arrin non-decreasing order. For a non-decreasing array, the conditionabs(arr[i] - arr[i-1]) <= 1simplifies toarr[i] <= arr[i-1] + 1. -
Set the first element: The first condition requires
arr[0]to be1. We enforce this by settingarr[0] = 1. Since the problem states all elements are positive, the originalarr[0](after sorting) is at least 1, so we only ever decrease it if it's larger than 1. -
Greedily adjust subsequent elements: We iterate from the second element (
i = 1). For eacharr[i], we must ensure it's no more thanarr[i-1] + 1. Ifarr[i]is already within this bound, we leave it, as we want to keep values high. Ifarr[i] > arr[i-1] + 1, we must decrease it. To maximize the final result, we perform the minimal decrease, settingarr[i] = arr[i-1] + 1. -
Return the maximum: After this process, the array satisfies all conditions, and since it's non-decreasing, the maximum element is the last one,
arr[arr.length - 1].
import java.util.Arrays; class Solution { public int maximumElementAfterDecrementingAndRearranging(int[] arr) { Arrays.sort(arr); arr[0] = 1; for (int i = 1; i < arr.length; i++) { if (arr[i] > arr[i - 1] + 1) { arr[i] = arr[i - 1] + 1; } } return arr[arr.length - 1]; }}Complexity
Time
O(N log N) The dominant operation is sorting the array, which typically takes O(N log N) time. The subsequent pass through the array takes O(N) time.
Space
O(log N) or O(N) This depends on the implementation of the sorting algorithm. In Java, `Arrays.sort` for primitive types has a space complexity of O(log N) for the recursion stack of quicksort. If a copy of the array is needed, it would be O(N).
Trade-offs
Pros
The logic is intuitive and relatively easy to implement.
If modifying the input array is allowed, the space complexity is very low (O(log N) for sort recursion stack).
Cons
The time complexity is dominated by the sorting step, making it less efficient than a linear-time solution.
This approach improves upon the sorting-based method by avoiding the O(N log N) sort. We observe that the exact values of numbers larger than the array's length n don't matter, as the maximum possible result is n. We can use a counting array to store the frequency of each number (capping large numbers at n). Then, we can iterate through the counts to greedily determine the longest possible sequence 1, 2, 3, ... which gives us the maximum element.
Algorithm
- Let
nbe the length of the array. Create a frequency arraycountsof sizen + 1. - Iterate through the input array
arr. For each numbernum, incrementcounts[min(num, n)]. This treats any number larger thannasn. - Initialize a variable
ans = 0, which will track the maximum element of the sequence1, 2, ...we can form. - Iterate with a variable
vfrom1ton. - In each iteration, update
ansusing the formula:ans = min(v, ans + counts[v]). This calculates the new length of our constructible sequence by adding the available numbers of valuev, capped by the valuevitself. - After the loop,
ansholds the maximum possible element value. Returnans.
Walkthrough
This method achieves linear time complexity by avoiding a full sort and instead using counting.
-
Observation and Capping: The maximum possible value in the final array cannot exceed its length,
n. An array like[1, 2, 3, ..., n]has a maximum ofn. Any number in the original arrayarr[i] > nis at least as useful asn, but no more useful, because the target value at any indexjis at mostj+1 <= n. Therefore, we can treat any number> nas if it weren. -
Frequency Counting: We create a
countsarray of sizen + 1. We iterate througharr, and for each numbernum, we incrementcounts[min(num, n)]. This takes O(N) time and O(N) space. -
Greedy Construction Simulation: We can now determine the maximum element without sorting. Let
ansbe the length of the valid prefix1, 2, ..., ansthat we can form. Initially,ans = 0. We iterate through the possible valuesvfrom1ton. At each stepv, we havecounts[v]numbers from the original array with valuev. These numbers, combined with the numbers we've already processed (which have formed a sequence up toans), can be used to extend our sequence. The total number of elements available to form a sequence isans + counts[v]. However, since the new numbers we are adding have a value ofv, the maximum value in the sequence we can form is capped atv. Therefore, the new length of our sequenceansbecomesmin(v, ans + counts[v]). -
Final Result: After iterating
vfrom1ton, the final value ofanswill be the maximum element possible in the modified array.
class Solution { public int maximumElementAfterDecrementingAndRearranging(int[] arr) { int n = arr.length; int[] counts = new int[n + 1]; for (int num : arr) { counts[Math.min(num, n)]++; } // ans represents the length of the prefix 1, 2, ..., ans we can form. int ans = 0; for (int v = 1; v <= n; v++) { // We have 'ans' elements from values < v, and 'counts[v]' elements with value v. // Total elements available are ans + counts[v]. // These can form a sequence up to length ans + counts[v]. // However, since the values are capped by v, the max element can't exceed v. ans = Math.min(v, ans + counts[v]); } return ans; }}Complexity
Time
O(N) The algorithm involves two main passes. The first pass populates the `counts` array, taking O(N) time. The second pass iterates from 1 to `n`, taking O(N) time. The total time complexity is O(N).
Space
O(N) We need an auxiliary array `counts` of size `n + 1` to store the frequencies of the numbers, where `n` is the length of the input array.
Trade-offs
Pros
Achieves optimal O(N) time complexity.
The logic is a clever way to bypass sorting by focusing on element counts.
Cons
Requires extra space proportional to the size of the input array, which could be a concern for very large N if memory is highly constrained.
Solutions
Solution
public class Solution { public int MaximumElementAfterDecrementingAndRearranging ( int [] arr ) { Array . Sort ( arr ); int n = arr . Length ; arr [ 0 ] = 1 ; for ( int i = 1 ; i < n ; ++ i ) { arr [ i ] = Math . Min ( arr [ i ], arr [ i - 1 ] + 1 ); } return arr [ n - 1 ]; } }class Solution {public int maximumElementAfterDecrementingAndRearranging(int[] arr) { Arrays.sort(arr); arr[0] = 1; int ans = 1; for (int i = 1; i < arr.length; ++i) { int d = Math.max(0, arr[i] - arr[i - 1] - 1); arr[i] -= d; ans = Math.max(ans, arr[i]); } return ans; }}class Solution {public: int maximumElementAfterDecrementingAndRearranging(vector<int> &arr) { sort(arr.begin(), arr.end()); arr[0] = 1; int ans = 1; for (int i = 1; i < arr.size(); ++i) { int d = max(0, arr[i] - arr[i - 1] - 1); arr[i] -= d; ans = max(ans, arr[i]); } return ans; }};class Solution: def maximumElementAfterDecrementingAndRearranging(self, arr: List[int]) -> int: arr . sort() arr[0] = 1 for i in range(1, len(arr)): d = max(0, arr[i] - arr[i - 1] - 1) arr[i] -= d return max(arr)Video walkthrough
Newsletter
One sharp idea, every week
System design and interview prep — short enough to finish.
No spam. Unsubscribe anytime.
Practice
Same difficulty — related problems to reinforce the pattern.