1243. Array Transformation 🔒
Problem Description
You are given an initial array arr
. Each day, you transform the array based on specific rules to create a new array for the next day.
The transformation rules for day i
(based on the array from day i-1
) are:
- Local minimum elements increase: If an element is smaller than both its left neighbor and its right neighbor, increment it by 1
- Local maximum elements decrease: If an element is larger than both its left neighbor and its right neighbor, decrement it by 1
- Boundary elements stay unchanged: The first and last elements of the array never change
You continue applying these transformations day after day until the array reaches a stable state where no more changes occur. Your task is to return this final stable array.
For example, if you have an array like [1, 6, 3, 4, 3, 5]
:
- Element at index 1 (value 6) is greater than both neighbors (1 and 3), so it decreases to 5
- Element at index 2 (value 3) is less than its left neighbor (6) but not less than its right neighbor (4), so it stays unchanged
- Element at index 4 (value 3) is less than both neighbors (4 and 5), so it increases to 4
- First element (1) and last element (5) remain unchanged
This process continues until no element satisfies the increment or decrement conditions, at which point the array has stabilized.
Intuition
The key insight is that this problem describes a stabilization process where local peaks get smoothed down and local valleys get filled up. Think of it like erosion and deposition in nature - high points gradually decrease while low points gradually increase until everything reaches equilibrium.
Since we don't know in advance how many days it will take for the array to stabilize, we need to simulate the transformation process day by day. The array will eventually stabilize because:
- Each local maximum can only decrease (moving closer to its neighbors)
- Each local minimum can only increase (moving closer to its neighbors)
- The first and last elements never change, providing fixed boundaries
This creates a convergence pattern where extreme values gradually moderate toward their neighbors' values. The process must terminate because each element has a finite range it can move within (bounded by its neighbors), and movements are always in one direction for each element until it no longer satisfies the condition.
The simulation approach naturally follows: we keep applying the transformation rules until we complete a full pass through the array without making any changes. We need to be careful to use a copy of the array when checking conditions, because all comparisons should be based on the previous day's values, not the partially updated current day's values. This is why we create a temporary copy t = arr[:]
and use it for all comparisons while updating the original arr
.
The flag variable f
tracks whether any change occurred during a pass. When f
remains False
after checking all elements, we know the array has stabilized and we can return it.
Solution Approach
The solution uses a simulation approach to model the daily transformations until the array stabilizes.
Algorithm Steps:
-
Initialize a flag
f = True
to track whether any changes occur during each day's transformation. -
Main simulation loop - Continue while
f
isTrue
:- Reset
f = False
at the start of each iteration - Create a copy of the current array:
t = arr[:]
. This copy preserves the original values for comparison while we modifyarr
- Reset
-
Process each internal element (indices 1 to
len(t) - 2
):- Check for local maximum: If
t[i] > t[i-1] and t[i] > t[i+1]
:- Decrement the element:
arr[i] -= 1
- Set
f = True
to indicate a change occurred
- Decrement the element:
- Check for local minimum: If
t[i] < t[i-1] and t[i] < t[i+1]
:- Increment the element:
arr[i] += 1
- Set
f = True
to indicate a change occurred
- Increment the element:
- Check for local maximum: If
-
Termination: When no changes occur in a complete pass (i.e.,
f
remainsFalse
), the loop exits and returns the stabilized array.
Key Implementation Details:
-
Why use a copy? The temporary array
t
ensures all comparisons for dayi
use values from dayi-1
. Without this, early modifications would affect later comparisons in the same iteration, leading to incorrect results. -
Boundary handling: The loop runs from index 1 to
len(t) - 2
, naturally excluding the first and last elements as required by the problem. -
Time Complexity:
O(n * k)
wheren
is the array length andk
is the number of days until stabilization. In the worst case,k
could be proportional to the maximum difference between adjacent elements. -
Space Complexity:
O(n)
for the temporary array copy used in each iteration.
Ready to land your dream job?
Unlock your dream job with a 3-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through the solution with the array [1, 6, 3, 4, 3, 5]
:
Initial State: arr = [1, 6, 3, 4, 3, 5]
Day 1 Transformation:
- Set
f = False
, create copyt = [1, 6, 3, 4, 3, 5]
- Check index 1 (value 6):
6 > 1
and6 > 3
→ local maximum → decrement to 5, setf = True
- Check index 2 (value 3):
3 < 6
but3 < 4
→ not a local extremum → no change - Check index 3 (value 4):
4 > 3
but4 > 3
→ not a local extremum → no change - Check index 4 (value 3):
3 < 4
and3 < 5
→ local minimum → increment to 4, setf = True
- Result:
arr = [1, 5, 3, 4, 4, 5]
,f = True
so continue
Day 2 Transformation:
- Set
f = False
, create copyt = [1, 5, 3, 4, 4, 5]
- Check index 1 (value 5):
5 > 1
and5 > 3
→ local maximum → decrement to 4, setf = True
- Check index 2 (value 3):
3 < 5
and3 < 4
→ local minimum → increment to 4, setf = True
- Check index 3 (value 4):
4 = 3
(after increment) and4 = 4
→ not a local extremum → no change - Check index 4 (value 4):
4 = 4
and4 < 5
→ not a local extremum → no change - Result:
arr = [1, 4, 4, 4, 4, 5]
,f = True
so continue
Day 3 Transformation:
- Set
f = False
, create copyt = [1, 4, 4, 4, 4, 5]
- Check index 1 (value 4):
4 > 1
but4 = 4
→ not a local maximum → no change - Check index 2 (value 4):
4 = 4
and4 = 4
→ not a local extremum → no change - Check index 3 (value 4):
4 = 4
and4 = 4
→ not a local extremum → no change - Check index 4 (value 4):
4 = 4
and4 < 5
→ not a local extremum → no change - Result:
arr = [1, 4, 4, 4, 4, 5]
,f = False
so array has stabilized
Final Answer: [1, 4, 4, 4, 4, 5]
The array stabilizes when the internal elements form a non-decreasing sequence between the fixed boundaries, with no element being strictly greater or strictly less than both its neighbors.
Solution Implementation
1class Solution:
2 def transformArray(self, arr: List[int]) -> List[int]:
3 """
4 Transform array by repeatedly adjusting elements based on neighbors.
5 Peak elements (greater than both neighbors) are decreased by 1.
6 Valley elements (less than both neighbors) are increased by 1.
7 Process continues until no more changes occur.
8
9 Args:
10 arr: Input array of integers
11
12 Returns:
13 Transformed array after stabilization
14 """
15 # Flag to track if any changes were made in current iteration
16 has_changed = True
17
18 # Continue transforming until no changes occur
19 while has_changed:
20 has_changed = False
21
22 # Create a copy of current array state for comparison
23 # This ensures all comparisons use the same iteration's values
24 temp_array = arr[:]
25
26 # Check each element except first and last (they have no two neighbors)
27 for i in range(1, len(temp_array) - 1):
28 # Check if current element is a local maximum (peak)
29 if temp_array[i] > temp_array[i - 1] and temp_array[i] > temp_array[i + 1]:
30 arr[i] -= 1 # Decrease peak element
31 has_changed = True
32
33 # Check if current element is a local minimum (valley)
34 if temp_array[i] < temp_array[i - 1] and temp_array[i] < temp_array[i + 1]:
35 arr[i] += 1 # Increase valley element
36 has_changed = True
37
38 return arr
39
1class Solution {
2 public List<Integer> transformArray(int[] arr) {
3 // Flag to track if any changes were made in current iteration
4 boolean hasChanged = true;
5
6 // Continue transforming until no more changes occur
7 while (hasChanged) {
8 hasChanged = false;
9
10 // Create a copy of current array to compare against
11 // This ensures all comparisons use the same iteration's values
12 int[] previousArray = arr.clone();
13
14 // Process all elements except first and last (they never change)
15 for (int i = 1; i < previousArray.length - 1; i++) {
16 // Check if current element is a local maximum
17 // If greater than both neighbors, decrease it
18 if (previousArray[i] > previousArray[i - 1] &&
19 previousArray[i] > previousArray[i + 1]) {
20 arr[i]--;
21 hasChanged = true;
22 }
23
24 // Check if current element is a local minimum
25 // If smaller than both neighbors, increase it
26 if (previousArray[i] < previousArray[i - 1] &&
27 previousArray[i] < previousArray[i + 1]) {
28 arr[i]++;
29 hasChanged = true;
30 }
31 }
32 }
33
34 // Convert the final array to a List<Integer> and return
35 List<Integer> result = new ArrayList<>();
36 for (int value : arr) {
37 result.add(value);
38 }
39
40 return result;
41 }
42}
43
1class Solution {
2public:
3 vector<int> transformArray(vector<int>& arr) {
4 // Continue transforming until no changes are made
5 bool hasChanged = true;
6
7 while (hasChanged) {
8 hasChanged = false;
9
10 // Create a copy of the current array to reference original values
11 vector<int> originalArray = arr;
12
13 // Iterate through interior elements (excluding first and last)
14 for (int i = 1; i < arr.size() - 1; ++i) {
15 // Check if current element is a local maximum
16 // If greater than both neighbors, decrease by 1
17 if (originalArray[i] > originalArray[i - 1] &&
18 originalArray[i] > originalArray[i + 1]) {
19 --arr[i];
20 hasChanged = true;
21 }
22
23 // Check if current element is a local minimum
24 // If smaller than both neighbors, increase by 1
25 if (originalArray[i] < originalArray[i - 1] &&
26 originalArray[i] < originalArray[i + 1]) {
27 ++arr[i];
28 hasChanged = true;
29 }
30 }
31 }
32
33 return arr;
34 }
35};
36
1/**
2 * Transforms an array by repeatedly adjusting interior elements based on their neighbors
3 * until the array reaches a stable state where no more changes occur.
4 *
5 * @param arr - The input array to transform
6 * @returns The transformed array after all adjustments are complete
7 */
8function transformArray(arr: number[]): number[] {
9 // Continue transforming until no changes are made
10 let hasChanged: boolean = true;
11
12 while (hasChanged) {
13 hasChanged = false;
14
15 // Create a copy of the current array to reference original values during iteration
16 const originalArray: number[] = [...arr];
17
18 // Iterate through interior elements (excluding first and last)
19 for (let i = 1; i < arr.length - 1; i++) {
20 // Check if current element is a local maximum
21 // If greater than both neighbors, decrease by 1
22 if (originalArray[i] > originalArray[i - 1] &&
23 originalArray[i] > originalArray[i + 1]) {
24 arr[i]--;
25 hasChanged = true;
26 }
27
28 // Check if current element is a local minimum
29 // If smaller than both neighbors, increase by 1
30 if (originalArray[i] < originalArray[i - 1] &&
31 originalArray[i] < originalArray[i + 1]) {
32 arr[i]++;
33 hasChanged = true;
34 }
35 }
36 }
37
38 return arr;
39}
40
Time and Space Complexity
Time Complexity: O(n × m)
The algorithm uses a while loop that continues until no changes are made to the array. In each iteration:
- We traverse the array once, taking
O(n)
time - The maximum number of iterations depends on how many times elements need to be adjusted
The key insight is that elements can only increase or decrease by 1 in each iteration. The worst case occurs when an element needs to change from its initial value to converge to a stable state. Since elements only change when they are local maxima (decrease by 1) or local minima (increase by 1), the maximum number of iterations is bounded by the maximum value m
in the array, as elements can at most decrease from m
to some stable value or increase to some stable value.
Therefore, the total time complexity is O(n × m)
.
Space Complexity: O(n)
The algorithm uses:
- A temporary array
t = arr[:]
which creates a copy of the input array, requiringO(n)
space - A few variables (
f
,i
) which requireO(1)
space
The dominant space usage is the temporary array copy, giving us a total space complexity of O(n)
.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Modifying the Array While Reading It
The most critical pitfall is directly comparing against the array while modifying it in the same iteration. Consider this incorrect implementation:
# INCORRECT - Don't do this!
for i in range(1, len(arr) - 1):
if arr[i] > arr[i-1] and arr[i] > arr[i+1]:
arr[i] -= 1
if arr[i] < arr[i-1] and arr[i] < arr[i+1]:
arr[i] += 1
Why it's wrong: When you modify arr[i]
, it affects the comparisons for arr[i+1]
in the next iteration. The element at index i+1
would be comparing against the already-modified value at index i
, not the original value from the start of this day's transformation.
Example of the bug:
- Array:
[1, 5, 3, 5, 1]
- At
i=1
:5 > 1
and5 > 3
, soarr[1]
becomes4
- At
i=2
: Now comparing3
with the modified4
(not original5
) and5
- This gives incorrect results since transformations should be simultaneous based on the previous day's state
Solution: Always create a copy of the array before starting modifications:
temp_array = arr[:] # Create a snapshot of current state
for i in range(1, len(temp_array) - 1):
# Compare using temp_array, modify arr
if temp_array[i] > temp_array[i-1] and temp_array[i] > temp_array[i+1]:
arr[i] -= 1
2. Using Separate If-Elif Instead of Two If Statements
Another subtle pitfall is using elif
for the second condition:
# POTENTIALLY PROBLEMATIC if temp_array[i] > temp_array[i-1] and temp_array[i] > temp_array[i+1]: arr[i] -= 1 elif temp_array[i] < temp_array[i-1] and temp_array[i] < temp_array[i+1]: # elif here arr[i] += 1
Why it could be an issue: While this works for the given problem (since an element cannot be both a local maximum and minimum simultaneously), using two separate if
statements is clearer and more maintainable. It explicitly shows that both conditions are independently checked.
Better approach: Use two independent if
statements as shown in the solution to make the logic clearer and avoid potential confusion.
3. Incorrect Boundary Handling
Attempting to check all elements including boundaries:
# INCORRECT - Will cause IndexError
for i in range(len(arr)): # Should be range(1, len(arr) - 1)
if arr[i] > arr[i-1] and arr[i] > arr[i+1]: # i-1 fails when i=0, i+1 fails when i=len(arr)-1
arr[i] -= 1
Solution: Always iterate from index 1 to len(arr) - 2
to ensure both neighbors exist.
What data structure does Breadth-first search typically uses to store intermediate states?
Recommended Readings
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
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Runtime Overview When learning about algorithms and data structures you'll frequently encounter the term time complexity This concept is fundamental in computer science and offers insights into how long an algorithm takes to complete given a certain input size What is Time Complexity Time complexity represents the amount of time
Want a Structured Path to Master System Design Too? Don’t Miss This!