Facebook Pixel

1144. Decrease Elements To Make Array Zigzag

Problem Description

You are given an array nums of integers. In one move, you can choose any element and decrease it by 1.

A zigzag array is an array that satisfies one of these two patterns:

  1. Even-indexed elements are peaks: Every element at an even index is greater than its adjacent elements. This creates a pattern like: A[0] > A[1] < A[2] > A[3] < A[4] > ...

  2. Odd-indexed elements are peaks: Every element at an odd index is greater than its adjacent elements. This creates a pattern like: A[0] < A[1] > A[2] < A[3] > A[4] < ...

Your task is to find the minimum number of moves needed to transform the given array into a zigzag array.

For example, if nums = [1, 2, 3]:

  • To make even-indexed elements peaks: We need nums[0] > nums[1] and nums[2] > nums[1]. The array [1, 0, 3] works, requiring 2 moves (decreasing nums[1] by 2).
  • To make odd-indexed elements peaks: We need nums[1] > nums[0] and nums[1] > nums[2]. The array [1, 2, 1] works, requiring 2 moves (decreasing nums[2] by 2).
  • The minimum is 2 moves.

The solution considers both possible zigzag patterns and calculates the cost for each. For each pattern, it iterates through positions that should be "valleys" (smaller than neighbors) and calculates how much to decrease them to satisfy the zigzag property. The answer is the minimum cost between the two patterns.

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

Intuition

The key insight is that we can only decrease elements, never increase them. This constraint actually simplifies our problem significantly.

Since we want to create a zigzag pattern, we have exactly two choices:

  1. Make elements at even indices the "peaks" (larger than neighbors)
  2. Make elements at odd indices the "peaks" (larger than neighbors)

Here's the crucial observation: if we decide which positions should be peaks, then the other positions must be "valleys" (smaller than neighbors). Since we can only decrease values, the peaks should remain unchanged - we achieve the zigzag pattern by lowering the valleys.

For any valley position, we need to ensure it's smaller than both its neighbors. If the current value at a valley position is nums[j], and its neighbors are nums[j-1] and nums[j+1], then we need:

  • nums[j] < nums[j-1], which means we need to decrease by at least nums[j] - nums[j-1] + 1 if nums[j] >= nums[j-1]
  • nums[j] < nums[j+1], which means we need to decrease by at least nums[j] - nums[j+1] + 1 if nums[j] >= nums[j+1]

We take the maximum of these requirements to satisfy both conditions simultaneously.

The greedy approach works because:

  1. We never need to decrease peak positions (doing so would only make things harder)
  2. For each valley, we decrease it by the minimum amount necessary to be smaller than its neighbors
  3. Each position's adjustment is independent - lowering one valley doesn't affect the requirements for other valleys

By calculating the total cost for both patterns (even peaks vs odd peaks) and taking the minimum, we find the optimal solution.

Learn more about Greedy patterns.

Solution Approach

The implementation uses enumeration and greedy strategy to find the minimum moves needed.

We maintain an array ans = [0, 0] where:

  • ans[0] stores the total moves needed when even-indexed elements are valleys
  • ans[1] stores the total moves needed when odd-indexed elements are valleys

The outer loop for i in range(2) handles both patterns:

  • When i = 0: We process even indices (0, 2, 4, ...) as valleys
  • When i = 1: We process odd indices (1, 3, 5, ...) as valleys

For each pattern, we iterate through the valley positions using for j in range(i, n, 2). This clever iteration starts at index i and jumps by 2 each time, visiting all positions that should be valleys.

At each valley position j, we calculate the required decrease amount d:

  1. Left neighbor check: If j > 0, we need nums[j] < nums[j-1]. If currently nums[j] >= nums[j-1], we must decrease by at least nums[j] - nums[j-1] + 1. We update d = max(d, nums[j] - nums[j-1] + 1).

  2. Right neighbor check: If j < n - 1, we need nums[j] < nums[j+1]. If currently nums[j] >= nums[j+1], we must decrease by at least nums[j] - nums[j+1] + 1. We update d = max(d, nums[j] - nums[j+1] + 1).

The max operation ensures we decrease enough to be smaller than both neighbors. If d remains 0, it means the current element is already smaller than its neighbors, requiring no moves.

We accumulate the total moves for each pattern: ans[i] += d.

Finally, we return min(ans) to get the minimum moves between the two possible zigzag patterns.

The time complexity is O(n) as we visit each element at most twice, and space complexity is O(1) using only constant extra space.

Ready to land your dream job?

Unlock your dream job with a 5-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's walk through the example nums = [2, 7, 10, 9, 8, 9] to illustrate the solution approach.

Pattern 1: Even indices as valleys (positions 0, 2, 4 should be smaller than neighbors)

  • Position 0 (value = 2):

    • No left neighbor
    • Right neighbor at position 1 has value 7
    • Need: nums[0] < nums[1] β†’ 2 < 7 βœ“ Already satisfied
    • Decrease needed: 0
  • Position 2 (value = 10):

    • Left neighbor at position 1 has value 7
    • Right neighbor at position 3 has value 9
    • Need: nums[2] < nums[1] β†’ 10 < 7 βœ— Must decrease by 10 - 7 + 1 = 4
    • Need: nums[2] < nums[3] β†’ 10 < 9 βœ— Must decrease by 10 - 9 + 1 = 2
    • Decrease needed: max(4, 2) = 4
  • Position 4 (value = 8):

    • Left neighbor at position 3 has value 9
    • Right neighbor at position 5 has value 9
    • Need: nums[4] < nums[3] β†’ 8 < 9 βœ“ Already satisfied
    • Need: nums[4] < nums[5] β†’ 8 < 9 βœ“ Already satisfied
    • Decrease needed: 0

Total moves for Pattern 1: 0 + 4 + 0 = 4 Result: [2, 7, 6, 9, 8, 9] (peak-valley-peak-valley-peak pattern)

Pattern 2: Odd indices as valleys (positions 1, 3, 5 should be smaller than neighbors)

  • Position 1 (value = 7):

    • Left neighbor at position 0 has value 2
    • Right neighbor at position 2 has value 10
    • Need: nums[1] < nums[0] β†’ 7 < 2 βœ— Must decrease by 7 - 2 + 1 = 6
    • Need: nums[1] < nums[2] β†’ 7 < 10 βœ“ Already satisfied
    • Decrease needed: max(6, 0) = 6
  • Position 3 (value = 9):

    • Left neighbor at position 2 has value 10
    • Right neighbor at position 4 has value 8
    • Need: nums[3] < nums[2] β†’ 9 < 10 βœ“ Already satisfied
    • Need: nums[3] < nums[4] β†’ 9 < 8 βœ— Must decrease by 9 - 8 + 1 = 2
    • Decrease needed: max(0, 2) = 2
  • Position 5 (value = 9):

    • Left neighbor at position 4 has value 8
    • No right neighbor
    • Need: nums[5] < nums[4] β†’ 9 < 8 βœ— Must decrease by 9 - 8 + 1 = 2
    • Decrease needed: 2

Total moves for Pattern 2: 6 + 2 + 2 = 10 Result: [2, 1, 10, 7, 8, 7] (valley-peak-valley-peak-valley pattern)

Final Answer: min(4, 10) = 4 moves

The optimal solution transforms the array to [2, 7, 6, 9, 8, 9] using 4 moves by making even indices valleys.

Solution Implementation

1class Solution:
2    def movesToMakeZigzag(self, nums: List[int]) -> int:
3        """
4        Find minimum moves to make array zigzag pattern.
5        A zigzag array satisfies either:
6        - nums[0] > nums[1] < nums[2] > nums[3] < ... (even indices are peaks)
7        - nums[0] < nums[1] > nums[2] < nums[3] > ... (odd indices are peaks)
8      
9        Args:
10            nums: List of integers to be modified
11          
12        Returns:
13            Minimum number of moves (decrements) needed
14        """
15        # Store moves needed for each pattern:
16        # moves_needed[0]: moves to make even indices peaks
17        # moves_needed[1]: moves to make odd indices peaks
18        moves_needed = [0, 0]
19        n = len(nums)
20      
21        # Try both patterns
22        for pattern_type in range(2):
23            # Process indices that should be peaks in current pattern
24            # pattern_type=0: process even indices (0, 2, 4, ...)
25            # pattern_type=1: process odd indices (1, 3, 5, ...)
26            for peak_index in range(pattern_type, n, 2):
27                # Calculate decrease needed for neighbors to be smaller than current peak
28                decrease_amount = 0
29              
30                # Check left neighbor if it exists
31                if peak_index > 0:
32                    # If left neighbor >= current, need to decrease current
33                    # by (neighbor_value - current_value + 1) to make current > neighbor
34                    decrease_amount = max(decrease_amount, nums[peak_index] - nums[peak_index - 1] + 1)
35              
36                # Check right neighbor if it exists
37                if peak_index < n - 1:
38                    # If right neighbor >= current, need to decrease current
39                    # by (neighbor_value - current_value + 1) to make current > neighbor
40                    decrease_amount = max(decrease_amount, nums[peak_index] - nums[peak_index + 1] + 1)
41              
42                # Add total decrease needed for this index
43                moves_needed[pattern_type] += decrease_amount
44      
45        # Return minimum moves between the two patterns
46        return min(moves_needed)
47
1class Solution {
2    public int movesToMakeZigzag(int[] nums) {
3        // Store the number of moves needed for each strategy:
4        // movesNeeded[0]: moves to make even-indexed elements smaller (peaks at odd indices)
5        // movesNeeded[1]: moves to make odd-indexed elements smaller (peaks at even indices)
6        int[] movesNeeded = new int[2];
7        int arrayLength = nums.length;
8      
9        // Try both strategies: starting from index 0 (even) and index 1 (odd)
10        for (int startIndex = 0; startIndex < 2; startIndex++) {
11            // Process elements at positions: startIndex, startIndex+2, startIndex+4, ...
12            // These are the elements we want to make smaller than their neighbors
13            for (int currentIndex = startIndex; currentIndex < arrayLength; currentIndex += 2) {
14                int decrementNeeded = 0;
15              
16                // Check left neighbor: ensure current element is smaller than left neighbor
17                if (currentIndex > 0) {
18                    // Calculate how much to decrease current element to be smaller than left neighbor
19                    decrementNeeded = Math.max(decrementNeeded, nums[currentIndex] - nums[currentIndex - 1] + 1);
20                }
21              
22                // Check right neighbor: ensure current element is smaller than right neighbor
23                if (currentIndex < arrayLength - 1) {
24                    // Calculate how much to decrease current element to be smaller than right neighbor
25                    decrementNeeded = Math.max(decrementNeeded, nums[currentIndex] - nums[currentIndex + 1] + 1);
26                }
27              
28                // Accumulate the total moves needed for this strategy
29                movesNeeded[startIndex] += decrementNeeded;
30            }
31        }
32      
33        // Return the minimum moves between the two strategies
34        return Math.min(movesNeeded[0], movesNeeded[1]);
35    }
36}
37
1class Solution {
2public:
3    int movesToMakeZigzag(vector<int>& nums) {
4        // Store the number of moves needed for two scenarios:
5        // moves[0]: make even-indexed elements smaller than their neighbors
6        // moves[1]: make odd-indexed elements smaller than their neighbors
7        vector<int> moves(2);
8        int n = nums.size();
9      
10        // Try both scenarios: starting with even indices (i=0) and odd indices (i=1)
11        for (int startIndex = 0; startIndex < 2; ++startIndex) {
12            // Process elements at positions startIndex, startIndex+2, startIndex+4, ...
13            for (int currentPos = startIndex; currentPos < n; currentPos += 2) {
14                int requiredDecrease = 0;
15              
16                // Check if current element needs to be decreased to be smaller than left neighbor
17                if (currentPos > 0) {
18                    requiredDecrease = max(requiredDecrease, nums[currentPos] - nums[currentPos - 1] + 1);
19                }
20              
21                // Check if current element needs to be decreased to be smaller than right neighbor
22                if (currentPos < n - 1) {
23                    requiredDecrease = max(requiredDecrease, nums[currentPos] - nums[currentPos + 1] + 1);
24                }
25              
26                // Add the required decrease for this position to the total moves
27                moves[startIndex] += requiredDecrease;
28            }
29        }
30      
31        // Return the minimum number of moves between the two scenarios
32        return min(moves[0], moves[1]);
33    }
34};
35
1/**
2 * Calculates minimum moves to make array zigzag pattern
3 * A zigzag array satisfies either:
4 * - nums[0] > nums[1] < nums[2] > nums[3] < ... (even indices are peaks)
5 * - nums[0] < nums[1] > nums[2] < nums[3] > ... (odd indices are peaks)
6 * 
7 * @param nums - Input array of numbers
8 * @returns Minimum number of moves needed to make zigzag pattern
9 */
10function movesToMakeZigzag(nums: number[]): number {
11    // Store the total moves needed for each strategy:
12    // strategyMoves[0]: make even indices as peaks
13    // strategyMoves[1]: make odd indices as peaks
14    const strategyMoves: number[] = Array(2).fill(0);
15    const arrayLength: number = nums.length;
16  
17    // Try both strategies: starting with even indices (i=0) and odd indices (i=1)
18    for (let strategyIndex = 0; strategyIndex < 2; ++strategyIndex) {
19        // Process positions based on current strategy (even or odd indices)
20        for (let currentPosition = strategyIndex; currentPosition < arrayLength; currentPosition += 2) {
21            let decrementNeeded: number = 0;
22          
23            // Check if we need to decrease left neighbor
24            if (currentPosition > 0) {
25                // Calculate how much to decrease left neighbor to make it smaller than current
26                decrementNeeded = Math.max(decrementNeeded, nums[currentPosition] - nums[currentPosition - 1] + 1);
27            }
28          
29            // Check if we need to decrease right neighbor
30            if (currentPosition < arrayLength - 1) {
31                // Calculate how much to decrease right neighbor to make it smaller than current
32                decrementNeeded = Math.max(decrementNeeded, nums[currentPosition] - nums[currentPosition + 1] + 1);
33            }
34          
35            // Accumulate the moves needed for this strategy
36            strategyMoves[strategyIndex] += decrementNeeded;
37        }
38    }
39  
40    // Return the minimum moves between the two strategies
41    return Math.min(...strategyMoves);
42}
43

Time and Space Complexity

The time complexity is O(n), where n is the length of the array nums. The algorithm uses two nested loops where the outer loop runs exactly 2 iterations (for i in range(2)), and the inner loop iterates through the array with step size 2. For each value of i, the inner loop visits approximately n/2 elements. Therefore, the total number of operations is 2 * (n/2) = n, resulting in linear time complexity.

The space complexity is O(1). The algorithm only uses a fixed amount of extra space: the list ans with exactly 2 elements, and a few integer variables (n, i, j, d). The space usage does not grow with the input size, making it constant space complexity.

Learn more about how to find time and space complexity quickly.

Common Pitfalls

1. Misunderstanding Which Elements to Modify

A critical pitfall is misunderstanding which elements should be decreased. The code appears to decrease elements at "peak" positions, but it's actually decreasing elements at "valley" positions.

The Bug: The variable naming and comments are misleading. When pattern_type = 0, we're actually processing even indices as valleys (not peaks), meaning we want even indices to be smaller than their neighbors.

Incorrect Understanding:

# Wrong interpretation: "Making even indices peaks"
for peak_index in range(pattern_type, n, 2):  # Misleading name!
    decrease_amount = 0
    # This logic actually ensures current element becomes SMALLER than neighbors

Correct Understanding:

# Correct: Making even indices valleys (odd indices become peaks)
for valley_index in range(pattern_type, n, 2):
    decrease_needed = 0
    # Ensure valley is smaller than both neighbors
    if valley_index > 0:
        # Need: nums[valley] < nums[valley-1]
        if nums[valley_index] >= nums[valley_index - 1]:
            decrease_needed = max(decrease_needed, nums[valley_index] - nums[valley_index - 1] + 1)

2. Incorrect Decrease Calculation

Another pitfall is calculating the wrong decrease amount. The code calculates how much to decrease the current element to be smaller than neighbors, not how much to decrease the neighbors.

Example to Illustrate:

  • Array: [2, 5, 3]
  • To make index 1 a valley: We need nums[1] < nums[0] and nums[1] < nums[2]
  • Current: nums[1] = 5, nums[0] = 2, nums[2] = 3
  • Decrease needed: max(5 - 2 + 1, 5 - 3 + 1) = max(4, 3) = 4
  • Result: Decrease nums[1] by 4 to get [2, 1, 3]

3. Pattern Confusion

The two patterns being tested are:

  • Pattern 0: Even indices are valleys β†’ Odd indices are peaks β†’ [small, LARGE, small, LARGE, ...]
  • Pattern 1: Odd indices are valleys β†’ Even indices are peaks β†’ [LARGE, small, LARGE, small, ...]

Corrected Solution with Clear Naming:

class Solution:
    def movesToMakeZigzag(self, nums: List[int]) -> int:
        """
        Find minimum moves to make array zigzag pattern.
        Two possible patterns:
        1. Even valleys: nums[0] < nums[1] > nums[2] < nums[3] > ...
        2. Odd valleys:  nums[0] > nums[1] < nums[2] > nums[3] < ...
        """
        moves = [0, 0]  # [moves for even valleys, moves for odd valleys]
        n = len(nums)
      
        for pattern in range(2):
            # pattern=0: make even indices valleys (odd indices peaks)
            # pattern=1: make odd indices valleys (even indices peaks)
            for valley_idx in range(pattern, n, 2):
                decrease = 0
              
                # Check if valley needs to be smaller than left neighbor
                if valley_idx > 0 and nums[valley_idx] >= nums[valley_idx - 1]:
                    decrease = max(decrease, nums[valley_idx] - nums[valley_idx - 1] + 1)
              
                # Check if valley needs to be smaller than right neighbor
                if valley_idx < n - 1 and nums[valley_idx] >= nums[valley_idx + 1]:
                    decrease = max(decrease, nums[valley_idx] - nums[valley_idx + 1] + 1)
              
                moves[pattern] += decrease
      
        return min(moves)

The key insight is that we're always decreasing elements at valley positions to ensure they're strictly smaller than their neighbors, not making peaks higher.

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

Which of the following shows the order of node visit in a Breadth-first Search?


Recommended Readings

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

Load More