Facebook Pixel

2765. Longest Alternating Subarray

EasyArrayEnumeration
Leetcode Link

Problem Description

You are given a 0-indexed integer array nums. Your task is to find the maximum length of an alternating subarray in this array.

An alternating subarray is defined as a contiguous subarray s of length m that meets these conditions:

  1. The length m must be greater than 1
  2. The second element equals the first element plus 1: s[1] = s[0] + 1
  3. The elements follow an alternating pattern where consecutive differences alternate between +1 and -1:
    • s[1] - s[0] = 1
    • s[2] - s[1] = -1
    • s[3] - s[2] = 1
    • s[4] - s[3] = -1
    • And so on...

In other words, the subarray follows the pattern [s[0], s[0]+1, s[0], s[0]+1, ...] where the values alternate between two consecutive integers.

For example:

  • [3, 4, 3, 4] is an alternating subarray (differences: +1, -1, +1)
  • [5, 6, 5, 6, 5] is an alternating subarray (differences: +1, -1, +1, -1)
  • [1, 2, 3] is NOT alternating (differences: +1, +1)

Return the length of the longest alternating subarray found in nums. If no such subarray exists, return -1.

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

Intuition

The key observation is that an alternating subarray must start with two consecutive elements where the second element is exactly 1 greater than the first. This gives us our starting point.

Once we identify a potential starting position, we need to check how far the alternating pattern extends. The pattern requires differences between consecutive elements to alternate between +1 and -1. We can track this by using a variable that flips between these two values.

Since we need to find the maximum length alternating subarray, we should check all possible starting positions in the array. For each position i, we try to extend the alternating subarray as far as possible.

The approach is to:

  1. Try each index as a potential starting point of an alternating subarray
  2. From each starting point, extend the subarray as long as the alternating pattern holds
  3. Keep track of the expected difference (either +1 or -1) at each step
  4. When the pattern breaks, record the length if it's greater than 1
  5. Update our maximum length found so far

The variable k in the solution cleverly handles the alternating differences. It starts at 1 (expecting a +1 difference), and after each successful match, we flip it to -1 or back to 1 using k *= -1. This way, we automatically expect the correct difference for the next pair of elements.

By trying all possible starting positions and extending each as far as possible, we ensure we find the longest alternating subarray in the array.

Solution Approach

The solution uses an enumeration approach where we check every possible starting position for an alternating subarray.

Implementation Details:

  1. Initialize variables: We set ans = -1 to track the maximum length found (defaulting to -1 if no valid subarray exists), and get the array length n.

  2. Enumerate starting positions: We iterate through each index i from 0 to n-1 as a potential starting point of an alternating subarray.

  3. For each starting position i:

    • Initialize k = 1 to track the expected difference (starts with expecting +1)
    • Set j = i as the current ending position of the subarray
    • Use a while loop to extend the subarray as long as possible:
      while j + 1 < n and nums[j + 1] - nums[j] == k:
          j += 1
          k *= -1
    • The loop continues while:
      • We haven't reached the end of the array (j + 1 < n)
      • The difference between consecutive elements matches our expected value k
    • After each successful match, we move j forward and flip k using k *= -1 to alternate between expecting +1 and -1
  4. Update the answer: After the while loop ends, we have found a potential alternating subarray from index i to j. If the length j - i + 1 is greater than 1 (meeting the requirement), we update our answer:

    if j - i + 1 > 1:
        ans = max(ans, j - i + 1)
  5. Return the result: After checking all possible starting positions, we return ans, which contains the maximum length found or -1 if no valid alternating subarray exists.

Time Complexity: O(n²) in the worst case, where we might check each element as a starting point and potentially traverse the rest of the array.

Space Complexity: O(1) as we only use a constant amount of extra space for variables.

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 solution with the array nums = [2, 3, 4, 3, 4].

Step 1: Initialize

  • ans = -1 (no alternating subarray found yet)
  • n = 5 (length of array)

Step 2: Try each starting position

Starting at i = 0 (value = 2):

  • Set k = 1 (expecting +1 difference), j = 0
  • Check: nums[1] - nums[0] = 3 - 2 = 1 ✓ (matches k=1)
    • Move to j = 1, flip k = -1
  • Check: nums[2] - nums[1] = 4 - 3 = 1 ✗ (doesn't match k=-1)
    • Stop extending
  • Subarray found: [2, 3], length = 2
  • Update: ans = max(-1, 2) = 2

Starting at i = 1 (value = 3):

  • Set k = 1, j = 1
  • Check: nums[2] - nums[1] = 4 - 3 = 1 ✓ (matches k=1)
    • Move to j = 2, flip k = -1
  • Check: nums[3] - nums[2] = 3 - 4 = -1 ✓ (matches k=-1)
    • Move to j = 3, flip k = 1
  • Check: nums[4] - nums[3] = 4 - 3 = 1 ✓ (matches k=1)
    • Move to j = 4, flip k = -1
  • Check: j + 1 = 5 (reached end of array)
    • Stop extending
  • Subarray found: [3, 4, 3, 4], length = 4
  • Update: ans = max(2, 4) = 4

Starting at i = 2 (value = 4):

  • Set k = 1, j = 2
  • Check: nums[3] - nums[2] = 3 - 4 = -1 ✗ (doesn't match k=1)
    • Stop extending
  • Subarray length = 1 (not valid, need length > 1)
  • No update to ans

Starting at i = 3 (value = 3):

  • Set k = 1, j = 3
  • Check: nums[4] - nums[3] = 4 - 3 = 1 ✓ (matches k=1)
    • Move to j = 4, flip k = -1
  • Check: j + 1 = 5 (reached end of array)
    • Stop extending
  • Subarray found: [3, 4], length = 2
  • Update: ans = max(4, 2) = 4

Starting at i = 4 (value = 4):

  • Set k = 1, j = 4
  • Check: j + 1 = 5 (reached end of array)
    • Stop extending
  • Subarray length = 1 (not valid)
  • No update to ans

Step 3: Return result

  • Return ans = 4

The longest alternating subarray is [3, 4, 3, 4] with length 4, where the differences between consecutive elements are [+1, -1, +1], perfectly alternating as required.

Solution Implementation

1class Solution:
2    def alternatingSubarray(self, nums: List[int]) -> int:
3        """
4        Find the maximum length of a subarray where consecutive elements
5        have alternating differences of 1 and -1.
6      
7        Args:
8            nums: List of integers
9          
10        Returns:
11            Maximum length of alternating subarray, or -1 if none exists
12        """
13        max_length = -1
14        n = len(nums)
15      
16        # Try each position as a potential starting point
17        for start_index in range(n):
18            # Expected difference pattern: starts with 1, then alternates to -1, 1, -1, ...
19            expected_diff = 1
20            current_index = start_index
21          
22            # Extend the subarray while the alternating pattern holds
23            while current_index + 1 < n and nums[current_index + 1] - nums[current_index] == expected_diff:
24                current_index += 1
25                # Flip the expected difference for next iteration (1 -> -1 or -1 -> 1)
26                expected_diff *= -1
27          
28            # Calculate the length of the alternating subarray found
29            subarray_length = current_index - start_index + 1
30          
31            # Update max_length if we found a valid alternating subarray (length > 1)
32            if subarray_length > 1:
33                max_length = max(max_length, subarray_length)
34      
35        return max_length
36
1class Solution {
2    public int alternatingSubarray(int[] nums) {
3        // Initialize the maximum length as -1 (no valid subarray found)
4        int maxLength = -1;
5        int n = nums.length;
6      
7        // Try each position as a potential starting point
8        for (int startIndex = 0; startIndex < n; startIndex++) {
9            // Initialize the expected difference (starts with 1)
10            int expectedDifference = 1;
11            int currentIndex = startIndex;
12          
13            // Extend the subarray while the alternating pattern holds
14            // Pattern: differences should alternate between 1 and -1
15            while (currentIndex + 1 < n && 
16                   nums[currentIndex + 1] - nums[currentIndex] == expectedDifference) {
17                // Flip the expected difference for next iteration (1 -> -1 -> 1 -> ...)
18                expectedDifference *= -1;
19                currentIndex++;
20            }
21          
22            // Calculate the length of the alternating subarray
23            int subarrayLength = currentIndex - startIndex + 1;
24          
25            // Update maximum length if we found a valid alternating subarray (length > 1)
26            if (subarrayLength > 1) {
27                maxLength = Math.max(maxLength, subarrayLength);
28            }
29        }
30      
31        return maxLength;
32    }
33}
34
1class Solution {
2public:
3    int alternatingSubarray(vector<int>& nums) {
4        int maxLength = -1;  // Initialize maximum length to -1 (no valid subarray found)
5        int n = nums.size(); // Total number of elements in the array
6      
7        // Iterate through each possible starting position
8        for (int startIdx = 0; startIdx < n; ++startIdx) {
9            int expectedDiff = 1;  // Expected difference between consecutive elements (alternates between 1 and -1)
10            int currentIdx = startIdx;  // Current index being checked
11          
12            // Extend the subarray as long as the alternating pattern holds
13            // Check if next element exists and maintains the alternating difference pattern
14            while (currentIdx + 1 < n && nums[currentIdx + 1] - nums[currentIdx] == expectedDiff) {
15                expectedDiff *= -1;  // Alternate the expected difference (1 -> -1 -> 1 -> ...)
16                ++currentIdx;  // Move to the next element
17            }
18          
19            // Calculate the length of the current alternating subarray
20            int currentLength = currentIdx - startIdx + 1;
21          
22            // Update maximum length if current subarray has at least 2 elements
23            if (currentLength > 1) {
24                maxLength = max(maxLength, currentLength);
25            }
26        }
27      
28        return maxLength;  // Return the maximum length found, or -1 if no valid subarray exists
29    }
30};
31
1/**
2 * Finds the maximum length of an alternating subarray where consecutive elements
3 * have differences alternating between 1 and -1
4 * @param nums - The input array of numbers
5 * @returns The maximum length of an alternating subarray, or -1 if none exists
6 */
7function alternatingSubarray(nums: number[]): number {
8    let maxLength: number = -1;
9    const arrayLength: number = nums.length;
10  
11    // Try each position as a potential starting point
12    for (let startIndex: number = 0; startIndex < arrayLength; ++startIndex) {
13        // Initialize expected difference (starts with 1, then alternates to -1, 1, -1, ...)
14        let expectedDifference: number = 1;
15        let currentIndex: number = startIndex;
16      
17        // Extend the subarray while the difference pattern holds
18        while (currentIndex + 1 < arrayLength && 
19               nums[currentIndex + 1] - nums[currentIndex] === expectedDifference) {
20            // Flip the expected difference for next iteration (1 -> -1 -> 1 -> ...)
21            expectedDifference *= -1;
22            currentIndex++;
23        }
24      
25        // Calculate subarray length and update maximum if valid
26        const subarrayLength: number = currentIndex - startIndex + 1;
27        if (subarrayLength > 1) {
28            maxLength = Math.max(maxLength, subarrayLength);
29        }
30    }
31  
32    return maxLength;
33}
34

Time and Space Complexity

The time complexity is O(n^2), where n is the length of the array nums. This is because we have an outer loop that iterates through all possible starting positions i from 0 to n-1, which takes O(n) iterations. For each starting position i, the inner while loop extends the subarray by checking consecutive elements. In the worst case, the inner loop can iterate up to n-1 times (when the entire array forms an alternating subarray starting from position 0). Therefore, the overall time complexity is O(n) * O(n) = O(n^2).

The space complexity is O(1). The algorithm only uses a constant amount of extra space for variables ans, n, i, j, and k, regardless of the input size. No additional data structures that scale with the input size are used.

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

Common Pitfalls

1. Inefficient Re-checking of Already Processed Subarrays

The Pitfall: The current solution checks every index as a potential starting point, even when we've already identified that index as part of a longer alternating subarray. For example, if we find an alternating subarray [3,4,3,4] starting at index i, we still check indices i+1, i+2, and i+3 as starting points, even though we already know the maximum alternating subarray starting from those positions.

Why It's Problematic:

  • Unnecessary iterations that don't contribute to finding a better answer
  • In arrays with many long alternating sequences, this leads to redundant work
  • While the time complexity remains O(n²), the actual runtime is worse than necessary

Solution: Skip ahead past the end of any alternating subarray we find, since any starting position within that subarray (except possibly the last element) cannot lead to a longer alternating subarray.

class Solution:
    def alternatingSubarray(self, nums: List[int]) -> int:
        max_length = -1
        n = len(nums)
        i = 0
      
        while i < n:
            expected_diff = 1
            j = i
          
            # Extend the subarray while the alternating pattern holds
            while j + 1 < n and nums[j + 1] - nums[j] == expected_diff:
                j += 1
                expected_diff *= -1
          
            subarray_length = j - i + 1
          
            if subarray_length > 1:
                max_length = max(max_length, subarray_length)
                # Skip to j instead of incrementing by 1
                # The last element might start a new alternating sequence
                i = j
            else:
                # No alternating subarray found starting at i
                i += 1
      
        return max_length

2. Missing Edge Case: Overlapping Alternating Patterns

The Pitfall: When skipping ahead after finding an alternating subarray, we might miss a potentially longer alternating subarray that starts at the last element of the current one. For example, in [1,2,1,2,3,4,3], after finding [1,2,1,2], if we skip to index 4, we miss the alternating subarray [2,3,4,3] starting at index 3.

Solution: When we find an alternating subarray of length > 2, check if the last element can start a new alternating pattern:

class Solution:
    def alternatingSubarray(self, nums: List[int]) -> int:
        max_length = -1
        n = len(nums)
        i = 0
      
        while i < n:
            expected_diff = 1
            j = i
          
            while j + 1 < n and nums[j + 1] - nums[j] == expected_diff:
                j += 1
                expected_diff *= -1
          
            subarray_length = j - i + 1
          
            if subarray_length > 1:
                max_length = max(max_length, subarray_length)
                # Check if the last element can start a new alternating sequence
                # This happens when the subarray length > 2
                if subarray_length > 2:
                    i = j  # Start from the last element of current subarray
                else:
                    i = j + 1  # Skip past the current subarray
            else:
                i += 1
      
        return max_length

These optimizations maintain correctness while improving the practical runtime of the algorithm.

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

What are the most two important steps in writing a depth first search function? (Select 2)


Recommended Readings

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

Load More