Facebook Pixel

1243. Array Transformation 🔒

EasyArraySimulation
Leetcode Link

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:

  1. Local minimum elements increase: If an element is smaller than both its left neighbor and its right neighbor, increment it by 1
  2. Local maximum elements decrease: If an element is larger than both its left neighbor and its right neighbor, decrement it by 1
  3. 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.

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

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:

  1. Each local maximum can only decrease (moving closer to its neighbors)
  2. Each local minimum can only increase (moving closer to its neighbors)
  3. 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:

  1. Initialize a flag f = True to track whether any changes occur during each day's transformation.

  2. Main simulation loop - Continue while f is True:

    • 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 modify arr
  3. 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
    • 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
  4. Termination: When no changes occur in a complete pass (i.e., f remains False), the loop exits and returns the stabilized array.

Key Implementation Details:

  • Why use a copy? The temporary array t ensures all comparisons for day i use values from day i-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) where n is the array length and k 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 Evaluator

Example 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 copy t = [1, 6, 3, 4, 3, 5]
  • Check index 1 (value 6): 6 > 1 and 6 > 3 → local maximum → decrement to 5, set f = True
  • Check index 2 (value 3): 3 < 6 but 3 < 4 → not a local extremum → no change
  • Check index 3 (value 4): 4 > 3 but 4 > 3 → not a local extremum → no change
  • Check index 4 (value 3): 3 < 4 and 3 < 5 → local minimum → increment to 4, set f = True
  • Result: arr = [1, 5, 3, 4, 4, 5], f = True so continue

Day 2 Transformation:

  • Set f = False, create copy t = [1, 5, 3, 4, 4, 5]
  • Check index 1 (value 5): 5 > 1 and 5 > 3 → local maximum → decrement to 4, set f = True
  • Check index 2 (value 3): 3 < 5 and 3 < 4 → local minimum → increment to 4, set f = True
  • Check index 3 (value 4): 4 = 3 (after increment) and 4 = 4 → not a local extremum → no change
  • Check index 4 (value 4): 4 = 4 and 4 < 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 copy t = [1, 4, 4, 4, 4, 5]
  • Check index 1 (value 4): 4 > 1 but 4 = 4 → not a local maximum → no change
  • Check index 2 (value 4): 4 = 4 and 4 = 4 → not a local extremum → no change
  • Check index 3 (value 4): 4 = 4 and 4 = 4 → not a local extremum → no change
  • Check index 4 (value 4): 4 = 4 and 4 < 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, requiring O(n) space
  • A few variables (f, i) which require O(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 and 5 > 3, so arr[1] becomes 4
  • At i=2: Now comparing 3 with the modified 4 (not original 5) and 5
  • 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.

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

What data structure does Breadth-first search typically uses to store intermediate states?


Recommended Readings

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

Load More