Want Help Cracking FAANG?

(Then click this)

×
Back to Question Bank

1243. Array Transformation - Leetcode Solution

Code Implementation

class Solution:
    def arrayTransform(self, arr):
        n = len(arr)
        while True:
            new_arr = arr[:]
            for i in range(1, n - 1):
                if arr[i] > arr[i - 1] and arr[i] > arr[i + 1]:
                    new_arr[i] -= 1
                elif arr[i] < arr[i - 1] and arr[i] < arr[i + 1]:
                    new_arr[i] += 1
            if new_arr == arr:
                break
            arr = new_arr
        return arr
      
class Solution {
public:
    vector<int> arrayTransform(vector<int>& arr) {
        int n = arr.size();
        while (true) {
            vector<int> new_arr = arr;
            for (int i = 1; i < n - 1; ++i) {
                if (arr[i] > arr[i-1] && arr[i] > arr[i+1]) {
                    new_arr[i]--;
                } else if (arr[i] < arr[i-1] && arr[i] < arr[i+1]) {
                    new_arr[i]++;
                }
            }
            if (new_arr == arr) break;
            arr = new_arr;
        }
        return arr;
    }
};
      
class Solution {
    public int[] arrayTransform(int[] arr) {
        int n = arr.length;
        while (true) {
            int[] newArr = arr.clone();
            for (int i = 1; i < n - 1; i++) {
                if (arr[i] > arr[i-1] && arr[i] > arr[i+1]) {
                    newArr[i]--;
                } else if (arr[i] < arr[i-1] && arr[i] < arr[i+1]) {
                    newArr[i]++;
                }
            }
            boolean changed = false;
            for (int i = 0; i < n; i++) {
                if (arr[i] != newArr[i]) {
                    changed = true;
                    break;
                }
            }
            if (!changed) break;
            arr = newArr;
        }
        return arr;
    }
}
      
var arrayTransform = function(arr) {
    let n = arr.length;
    while (true) {
        let newArr = arr.slice();
        for (let i = 1; i < n - 1; i++) {
            if (arr[i] > arr[i-1] && arr[i] > arr[i+1]) {
                newArr[i]--;
            } else if (arr[i] < arr[i-1] && arr[i] < arr[i+1]) {
                newArr[i]++;
            }
        }
        let changed = false;
        for (let i = 0; i < n; i++) {
            if (arr[i] !== newArr[i]) {
                changed = true;
                break;
            }
        }
        if (!changed) break;
        arr = newArr;
    }
    return arr;
};
      

Problem Description

Given an integer array arr, you repeatedly apply the following transformation to it:

  • For every element arr[i] where 1 ≤ i < arr.length - 1:
    • If arr[i] is strictly greater than both its neighbors, decrease it by 1.
    • If arr[i] is strictly less than both its neighbors, increase it by 1.
    • Otherwise, leave it unchanged.

Repeat this process until the array no longer changes. Return the final state of arr after all possible transformations.

Constraints:

  • There is always one valid solution.
  • Elements are not reused or skipped; all transformations in a round are based on the original array before the round.

Thought Process

The first step is to understand the transformation process: for each round, we look at every element (except the first and last), and based on its two neighbors, we decide whether to increment, decrement, or leave it alone. All transformations in a round must use the original values (not the updated ones from the same round).

A brute-force approach would be to simulate the process exactly as described: repeatedly scan the array, apply the rules, and repeat until nothing changes. However, we might wonder if there's a faster way—perhaps by recognizing that the process always converges to a stable state, and maybe we can reach it directly.

After closer inspection, the transformation is local (depends only on neighbors), and the process always stops after a finite number of steps because the array can't change forever. The brute-force simulation is acceptable for small arrays, but for larger ones, we want to avoid unnecessary work.

Solution Approach

We will use a direct simulation approach, applying the transformation rules until the array stops changing. Here is how we do it:

  1. Copy the Array: At the start of each round, make a copy of the current array. This ensures that all updates in the round are based on the original values, not partially updated ones.
  2. Iterate Over the Array: For each index i from 1 to n - 2:
    • If arr[i] is strictly greater than both arr[i-1] and arr[i+1], decrement new_arr[i].
    • If arr[i] is strictly less than both neighbors, increment new_arr[i].
    • Otherwise, leave new_arr[i] unchanged.
  3. Check for Changes: After processing, compare new_arr with the original arr. If they are the same, stop and return arr. Otherwise, repeat the process with new_arr.
  4. Why This Works: Every round, the array becomes "smoother"—peaks decrease, valleys increase, and eventually, no more changes are possible.

This approach is simple, easy to implement, and always finds the correct final state.

Example Walkthrough

Let's use the example input: arr = [6, 2, 3, 4, 5, 6]

  1. First Round:
    • Index 1: 2 is less than 6 and 3, so increment to 3.
    • Index 2: 3 is less than 2 and 4, so increment to 4.
    • Index 3: 4 is less than 3 and 5, so increment to 5.
    • Index 4: 5 is less than 4 and 6, so increment to 6.

    Array becomes: [6, 3, 4, 5, 6, 6]

  2. Second Round:
    • Index 1: 3 is less than 6 and 4, so increment to 4.
    • Index 2: 4 is less than 3 and 5, so increment to 5.
    • Index 3: 5 is less than 4 and 6, so increment to 6.
    • Index 4: 6 is not less than both neighbors, so unchanged.

    Array becomes: [6, 4, 5, 6, 6, 6]

  3. Third Round:
    • Index 1: 4 is less than 6 and 5, so increment to 5.
    • Index 2: 5 is less than 4 and 6, so increment to 6.
    • Index 3: 6 is not less than both neighbors, so unchanged.
    • Index 4: 6 is not less than both neighbors, so unchanged.

    Array becomes: [6, 5, 6, 6, 6, 6]

  4. Fourth Round:
    • Index 1: 5 is less than 6 and 6, so increment to 6.
    • Index 2: 6 is not less than both neighbors, so unchanged.
    • Index 3: 6 is not less than both neighbors, so unchanged.
    • Index 4: 6 is not less than both neighbors, so unchanged.

    Array becomes: [6, 6, 6, 6, 6, 6]

  5. Fifth Round:
    • No changes. The process stops.

    Final output: [6, 6, 6, 6, 6, 6]

Time and Space Complexity

Brute-force approach:

  • Each round takes O(n) time because we process each element once.
  • In the worst case, we might need O(max_diff * n) rounds, where max_diff is the difference between the largest and smallest element, as each element can only change by 1 per round.
  • So, worst-case time complexity is O(n^2) if the array is long and the difference is large.
  • Space complexity is O(n) for the copy of the array.
Optimized approach:
  • Because the process converges quickly (peaks and valleys are smoothed out rapidly), in practice, the number of rounds is much less than n, so the actual running time is often close to O(n).
  • No extra optimization is needed because the process is efficient for reasonable input sizes.

Summary

The Array Transformation problem is a classic example of simulating a local update process until stability is reached. By always using the original array for each round, we ensure correctness. The approach is simple, intuitive, and effective, requiring just a few lines of code and basic programming constructs. The key insight is that the process always converges, and the simulation directly implements the transformation as described.