LeetCode 1299 - Replace Elements with Greatest Element on Right Side
The problem gives us an integer array arr and asks us to replace every element with the greatest value that appears some
Difficulty: 🟢 Easy
Topics: Array
Solution
Problem Understanding
The problem gives us an integer array arr and asks us to replace every element with the greatest value that appears somewhere to its right. The final element in the array has no elements to its right, so it must always become -1.
In other words, for every index i, we want to compute:
- the maximum value among all elements from
i + 1to the end of the array - replace
arr[i]with that value
The last index has no valid range to examine, so its replacement value is -1.
For example, consider:
arr = [17,18,5,4,6,1]
For index 0, the elements to the right are [18,5,4,6,1], and the maximum is 18.
For index 1, the elements to the right are [5,4,6,1], and the maximum is 6.
Continuing this process produces:
[18,6,6,6,1,-1]
The constraints are relatively small:
1 <= arr.length <= 10^41 <= arr[i] <= 10^5
An O(n^2) solution would technically still pass for 10^4 in many environments, but it is inefficient and unnecessary. The problem has a clean linear-time solution.
Several edge cases are important:
- A single-element array such as
[400] - Arrays already in decreasing order
- Arrays already in increasing order
- Arrays where many elements are equal
- Very small arrays where index handling mistakes are common
The problem guarantees that the array contains at least one element, so we never need to handle an empty array.
Approaches
Brute Force Approach
The most direct solution is to process each index independently.
For every position i, we scan all elements to the right of i and compute the maximum value manually. After finding that maximum, we store it as the replacement value for arr[i].
For the last index, we simply assign -1.
This approach is correct because it explicitly checks every valid candidate to the right of each position. However, it repeatedly scans overlapping portions of the array.
For example:
Index 0 scans almost the entire array
Index 1 scans almost the entire remaining array
Index 2 scans slightly less
This repeated work leads to quadratic time complexity.
Optimal Approach
The key observation is that the maximum value to the right of an index can be maintained incrementally if we process the array from right to left.
Suppose we are standing at index i.
If we already know the greatest value seen so far among elements to the right, then:
- that value is exactly what
arr[i]should become - after processing index
i, we update the running maximum using the original value atarr[i]
This works because when traversing from right to left, we always have complete information about everything to the right of the current index.
Instead of rescanning the suffix repeatedly, we maintain one variable:
max_right
This variable always stores the greatest value encountered so far while moving backward through the array.
| Approach | Time Complexity | Space Complexity | Notes |
|---|---|---|---|
| Brute Force | O(n²) | O(1) | For each index, scan all elements to the right |
| Optimal | O(n) | O(1) | Traverse from right to left while maintaining running maximum |
Algorithm Walkthrough
- Initialize a variable called
max_rightto-1.
This variable represents the greatest element seen so far to the right of the current position. We start with -1 because the last element should become -1.
2. Traverse the array from right to left.
We move backward because each position depends on information from its right side. By the time we reach an index, we already know the maximum value among all later elements. 3. Store the current element temporarily.
Before overwriting arr[i], we must save its original value because it may become the new maximum for elements further left.
4. Replace arr[i] with max_right.
At this moment, max_right contains the largest value among all elements strictly to the right of index i.
5. Update max_right.
Compare the saved original value with the current max_right and keep the larger one.
6. Continue until the beginning of the array is reached.
Every element will eventually contain the greatest value to its right.
Why it works
The algorithm maintains an invariant:
Before processing index i,
max_right equals the maximum value among all elements to the right of i.
Because we process indices from right to left, this invariant is always true. We use max_right as the replacement value, then update it using the original current element so the invariant remains valid for the next iteration.
This guarantees correctness for every index.
Python Solution
from typing import List
class Solution:
def replaceElements(self, arr: List[int]) -> List[int]:
max_right = -1
for i in range(len(arr) - 1, -1, -1):
current = arr[i]
arr[i] = max_right
max_right = max(max_right, current)
return arr
The implementation follows the optimal right-to-left traversal strategy.
The variable max_right tracks the largest value encountered so far while moving backward through the array. Initially, it is -1 because the last element must become -1.
The loop iterates from the final index down to index 0. For each position:
- the original value is stored in
current - the array element is replaced with
max_right max_rightis updated using the original value
Saving the original value before overwriting is essential. Otherwise, we would lose information needed to update the running maximum.
The solution modifies the array in place, which keeps the space complexity constant.
Go Solution
func replaceElements(arr []int) []int {
maxRight := -1
for i := len(arr) - 1; i >= 0; i-- {
current := arr[i]
arr[i] = maxRight
if current > maxRight {
maxRight = current
}
}
return arr
}
The Go implementation is almost identical to the Python version.
Slices in Go are mutable, so the function updates the input slice directly. No additional array allocation is required.
There are no integer overflow concerns because the constraints are very small compared to Go's integer range.
The algorithm also naturally handles single-element arrays because the loop runs once, replacing the only value with -1.
Worked Examples
Example 1
Input:
arr = [17,18,5,4,6,1]
Initial state:
max_right = -1
| Index | Original Value | max_right Before | New Value | max_right After | Array State |
|---|---|---|---|---|---|
| 5 | 1 | -1 | -1 | 1 | [17,18,5,4,6,-1] |
| 4 | 6 | 1 | 1 | 6 | [17,18,5,4,1,-1] |
| 3 | 4 | 6 | 6 | 6 | [17,18,5,6,1,-1] |
| 2 | 5 | 6 | 6 | 6 | [17,18,6,6,1,-1] |
| 1 | 18 | 6 | 6 | 18 | [17,6,6,6,1,-1] |
| 0 | 17 | 18 | 18 | 18 | [18,6,6,6,1,-1] |
Final result:
[18,6,6,6,1,-1]
Example 2
Input:
arr = [400]
Initial state:
max_right = -1
| Index | Original Value | max_right Before | New Value | max_right After | Array State |
|---|---|---|---|---|---|
| 0 | 400 | -1 | -1 | 400 | [-1] |
Final result:
[-1]
Complexity Analysis
| Measure | Complexity | Explanation |
|---|---|---|
| Time | O(n) | Each element is processed exactly once |
| Space | O(1) | Only a few extra variables are used |
The algorithm performs a single traversal of the array from right to left. Each iteration does constant work, so the total running time grows linearly with the input size.
No auxiliary data structures are allocated. The input array itself is modified in place, which keeps the extra memory usage constant.
Test Cases
from typing import List
class Solution:
def replaceElements(self, arr: List[int]) -> List[int]:
max_right = -1
for i in range(len(arr) - 1, -1, -1):
current = arr[i]
arr[i] = max_right
max_right = max(max_right, current)
return arr
solution = Solution()
assert solution.replaceElements([17, 18, 5, 4, 6, 1]) == [18, 6, 6, 6, 1, -1] # Provided example
assert solution.replaceElements([400]) == [-1] # Single-element array
assert solution.replaceElements([1, 2, 3, 4]) == [4, 4, 4, -1] # Strictly increasing
assert solution.replaceElements([4, 3, 2, 1]) == [3, 2, 1, -1] # Strictly decreasing
assert solution.replaceElements([7, 7, 7, 7]) == [7, 7, 7, -1] # All equal values
assert solution.replaceElements([10, 5]) == [5, -1] # Two-element array
assert solution.replaceElements([5, 10]) == [10, -1] # Increasing two-element array
assert solution.replaceElements([100000, 1, 2]) == [2, 2, -1] # Large value at front
assert solution.replaceElements([1, 100000, 2]) == [100000, 2, -1] # Large middle value
assert solution.replaceElements([9]) == [-1] # Minimum valid length
| Test | Why |
|---|---|
[17,18,5,4,6,1] |
Validates the main example |
[400] |
Tests single-element handling |
[1,2,3,4] |
Tests increasing order |
[4,3,2,1] |
Tests decreasing order |
[7,7,7,7] |
Tests duplicate values |
[10,5] |
Tests small decreasing array |
[5,10] |
Tests small increasing array |
[100000,1,2] |
Tests large front value |
[1,100000,2] |
Tests large middle value |
[9] |
Tests smallest possible input size |
Edge Cases
Single-Element Array
A one-element array is the smallest valid input. Since there are no elements to the right, the answer must always be [-1].
This case often causes bugs in solutions that assume at least one right-side element exists. The implementation handles it naturally because the loop runs once with max_right = -1.
Strictly Increasing Array
Consider:
[1,2,3,4]
Every element should become the final element 4, except the last position.
This case is important because the running maximum changes repeatedly while traversing backward. The algorithm correctly updates max_right after each step.
Strictly Decreasing Array
Consider:
[4,3,2,1]
Each element's replacement is simply the next element to its right.
This case verifies that the algorithm does not incorrectly keep outdated maximum values. Since the running maximum changes at every step, correctness depends on proper update order.
Arrays with Duplicate Values
Consider:
[7,7,7,7]
The greatest value to the right remains 7 for all positions except the last.
Some incorrect implementations accidentally use strictly greater comparisons in a way that mishandles duplicates. This implementation uses max() correctly, so equal values are preserved properly.