2765. Longest Alternating Subarray
Problem Description
This LeetCode problem provides you with an integer array nums
which is indexed starting from 0. You are tasked with finding the longest subarray within this array that is deemed 'alternating'. A subarray is defined as alternating if it follows two main criteria:
- The length of the subarray,
m
, must be greater than 1. - The elements of the subarray must follow an alternating increment and decrement pattern. Specifically, the first element
s0
and the second elements1
should satisfy the conditions1 = s0 + 1
, and for subsequent elements, the difference should alternate between +1 and -1.
This alternating pattern implies that starting with the difference of +1 for the first pair, the next pair should have a difference of -1, followed by +1 for the next, and so on, effectively creating a sequence where the sign of the difference changes with each additional element.
You are required to return the maximum length of such an alternating subarray found in nums
. If no alternating subarray is present, you should return -1
.
An important note is that subarrays are continuous segments of the array and cannot be empty.
Intuition
The intuition behind the proposed solution is based on iterating over the array and examining each possible starting point for an alternating subarray. To effectively find alternating subarrays, we keep track of the required difference (+1
or -1
) at each step using a variable k
. For each element in the array, we look ahead to see how far the alternating pattern continues before it breaks.
We initiate the search from each index i
, which acts as the potential start of an alternating subarray. Using a second index j
, we move through the array while the difference between successive elements nums[j + 1]
and nums[j]
matches k
, the alternating difference we are currently looking for.
We start with k
set to 1 since s1
must be greater by 1 than s0
. As we advance through the array and find that the alternate difference holds true, we increment j
, invert k
, and continue. This allows us to track whether the next element should be higher or lower than the previous one.
Once we have found a subarray that starts at i
and ends at j
(where j - i + 1
is greater than 1) that satisfies the alternating condition, we compare its length with our current maximum length. If it's longer, we update the maximum length (ans
) to this new value. We repeat this process for every index i
in the array to make sure all possibilities are considered.
By rigorously checking every index as a potential start and extending the length of the subarray as long as the alternating condition holds, we can ensure the longest subarray is found.
Solution Approach
The provided solution follows a straightforward brute force enumeration approach to find the longest alternating subarray in nums
. Here is a step-by-step explanation of the approach:
- We initiate a variable
ans
to keep track of the maximum length of an alternating subarray found so far, starting with a value of-1
. - We also determine the length of the array
n
.
For each index i
in the range of 0
to n
, which represents the potential starting point of an alternating subarray:
- We set
k
to1
, which is the initial expected difference between the first and second elements of a potential alternating subarray. - We also set
j
toi
, withj
serving as an index to explore the array forward from the starting pointi
.
We then enter a loop to test the alternating pattern:
- While
j + 1 < n
(to prevent going out of bounds) andnums[j + 1] - nums[j] == k
(the adjacent elements meet the alternating condition), we incrementj
to continue extending the subarray and multiplyk
by-1
to switch the expected difference for the next pair of elements. - If the aforementioned while loop breaks, it means that either the end of the array is reached or the alternating pattern is not satisfied anymore.
After exiting the loop, we check if we have a valid subarray (of length greater than 1) by confirming j - i + 1 > 1
.
- If it's a valid subarray, we update
ans
to be the maximum of its current value andj - i + 1
, which is the length of the current alternating subarray.
At the end of the loop over i
, the variable ans
will hold the length of the longest alternating subarray found, or it will remain -1
if no such subarray exists.
This enumeration technique, combined with tracking of the alternating pattern with a variable k
, is effective in testing every possible subarray's start point and ensuring the maximal length subarray is identified. It's an exhaustive method, not relying on any specific algorithms or data structures beyond basic control flow and variable tracking, making it a brute force solution.
The absence of advanced data structures makes the code simple, but also means the time complexity is O(n^2) in the worst case, as for each starting point i
, we may potentially scan up to n - i
elements to find the longest subarray.
Ready to land your dream job?
Unlock your dream job with a 2-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's illustrate the solution approach with a small example using the following integer array nums
:
nums = [5, 6, 7, 8, 7, 6, 7, 8, 9]
Given the array, we need to find the maximum length of an alternating subarray following an increment-decrement pattern starting with +1.
-
Initialize
ans
as-1
. The lengthn
ofnums
is9
. -
For each index
i
starting from0
, initiate our search for an alternating subarray.- When
i = 0
, setk = 1
andj = 0
. - While loop:
- For
j = 0
,nums[1] - nums[0] = 6 - 5 = 1
, which is equal tok
. - Increment
j
to1
, andk
becomes-1
. - Now
j = 1
,nums[2] - nums[1] = 7 - 6 = 1
, which is not equal to the currentk
(-1), so the loop breaks.
- For
- The subarray from index
0
to1
has a length of 2 (j - i + 1
), so we updateans
to2
.
- When
-
Repeat this process for other starting points:
- When
i = 1
, the alternating subarray is6, 7, 8, 7, 6
, ending atj = 5
. Updateans
to5
. - Subsequent checks for starting points
i = 2
toi = 8
will reveal no longer subarray than the already found length5
.
- When
By following this algorithm, the final value of ans
is 5
, which is the length of the longest alternating subarray found in nums
, with the subarray being [6, 7, 8, 7, 6]
. If no such subarray existed, ans
would've remained -1
.
Solution Implementation
1class Solution:
2 def alternatingSubarray(self, nums: List[int]) -> int:
3 # Initialize the answer to -1 to handle cases where no alternating subarray is found.
4 max_length = -1
5 # Get the length of the input list of numbers.
6 n = len(nums)
7
8 # Iterate over the list to find all possible starting points of alternating subarrays.
9 for i in range(n):
10 # Initialize the sign change indicator to 1 for alternating checks.
11 sign = 1
12 # Initialize the pointer to track subarray length from current position.
13 end = i
14
15 # Go through the list while the following conditions is true:
16 # 1. The next position is within the array bounds.
17 # 2. Consecutive numbers have an alternating difference.
18 while end + 1 < n and nums[end + 1] - nums[end] == sign:
19 # Move to the next number in the subarray.
20 end += 1
21 # Change the sign to alternate.
22 sign *= -1
23
24 # Update the length of the longest alternating subarray found.
25 # Only consider subarrays with more than one element.
26 if end - i + 1 > 1:
27 max_length = max(max_length, end - i + 1)
28
29 # Return the length of the longest alternating subarray, or -1 if none are found.
30 return max_length
31
1class Solution {
2
3 // Method to find the length of the longest alternating subarray
4 public int alternatingSubarray(int[] nums) {
5 // Initialize the answer to -1 since we want to find the length
6 // and the minimum length of a valid alternating subarray is 2
7 int maxLengthOfAltSubarray = -1;
8 int arrayLength = nums.length;
9
10 // Iterate over the array to find all possible subarrays
11 for (int startIndex = 0; startIndex < arrayLength; ++startIndex) {
12 // Initialize the alternating difference factor for the subarray
13 int alternatingFactor = 1;
14 // `startIndex` is the beginning of the potential alternating subarray
15 int currentIndex = startIndex;
16
17 // Compare adjacent elements to check if they form an alternating subarray
18 for (; currentIndex + 1 < arrayLength && nums[currentIndex + 1] - nums[currentIndex] == alternatingFactor; ++currentIndex) {
19 // After each comparison, flip the alternating difference factor
20 alternatingFactor *= -1;
21 }
22
23 // Check if we found a valid alternating subarray of more than 1 element
24 if (currentIndex - startIndex + 1 > 1) {
25 // Update the answer if the current subarray is longer than the previous subarrays found
26 maxLengthOfAltSubarray = Math.max(maxLengthOfAltSubarray, currentIndex - startIndex + 1);
27 }
28 }
29 // Return the length of the longest alternating subarray found
30 return maxLengthOfAltSubarray;
31 }
32}
33
1class Solution {
2public:
3 // Function to find the length of the longest subarray
4 // where adjacent elements are alternately increasing and decreasing
5 int alternatingSubarray(vector<int>& nums) {
6 int maxLen = -1; // Variable to store the length of longest subarray, initialized to -1
7 int n = nums.size(); // Size of the input array
8 for (int i = 0; i < n; ++i) { // Loop over the array
9 int sequenceDiff = 1; // Initialize the difference that we are looking for (either 1 or -1)
10 int j = i; // Start of the current subarray
11 // Increase the length of the subarray while the difference between
12 // consecutive elements matches the expected difference
13 for (; j + 1 < n && nums[j + 1] - nums[j] == sequenceDiff; ++j) {
14 sequenceDiff *= -1; // Flip the expected difference for the next pair
15 }
16 // If we found a subarray of length greater than 1, update the answer
17 if (j - i + 1 > 1) {
18 maxLen = max(maxLen, j - i + 1);
19 }
20 }
21 // Return the length of the longest alternating subarray
22 return maxLen;
23 }
24};
25
1function alternatingSubarray(nums: number[]): number {
2 let maxSubarrayLength = -1; // Initialize the maximum subarray length to -1
3 const arrayLength = nums.length; // Get the length of the input array
4
5 // Iterate over the input array
6 for (let startIndex = 0; startIndex < arrayLength; ++startIndex) {
7 let alternatingDifference = 1; // This alternating difference changes from 1 to -1 and vice versa
8 let endIndex = startIndex; // Initialize the end index for the subarray to the start index
9
10 // Check if the current subarray alternates by checking the difference between consecutive elements
11 while (endIndex + 1 < arrayLength && nums[endIndex + 1] - nums[endIndex] === alternatingDifference) {
12 alternatingDifference *= -1; // Alternate the expected difference for the next pair
13 endIndex++; // Increment the end index of the current subarray
14 }
15
16 // If the length of the current subarray is greater than 1, update the maxSubarrayLength
17 if (endIndex - startIndex + 1 > 1) {
18 maxSubarrayLength = Math.max(maxSubarrayLength, endIndex - startIndex + 1);
19 }
20 }
21
22 return maxSubarrayLength; // Return the length of the longest alternating subarray
23}
24
Time and Space Complexity
The time complexity of the provided code is O(n^2)
, where n
is the length of the input array nums
. This is because the code includes a loop that iterates over each possible starting index i
for a subarray. For each starting index, the inner loop potentially iterates over the remaining part of the array to find the longest alternating subarray starting at that point. In the worst-case scenario, this takes O(n)
time for each starting index, and since there are n
possible starting indices, the overall complexity is n * O(n)
, which simplifies to O(n^2)
.
The space complexity of the algorithm is O(1)
, which means it is constant. This is because the memory usage does not depend on the size of the input array; the code only uses a fixed number of integer variables (ans
, n
, i
, k
, and j
) to compute the result.
Learn more about how to find time and space complexity quickly using problem constraints.
What's the output of running the following function using input [30, 20, 10, 100, 33, 12]
?
1def fun(arr: List[int]) -> List[int]:
2 import heapq
3 heapq.heapify(arr)
4 res = []
5 for i in range(3):
6 res.append(heapq.heappop(arr))
7 return res
8
1public static int[] fun(int[] arr) {
2 int[] res = new int[3];
3 PriorityQueue<Integer> heap = new PriorityQueue<>();
4 for (int i = 0; i < arr.length; i++) {
5 heap.add(arr[i]);
6 }
7 for (int i = 0; i < 3; i++) {
8 res[i] = heap.poll();
9 }
10 return res;
11}
12
1class HeapItem {
2 constructor(item, priority = item) {
3 this.item = item;
4 this.priority = priority;
5 }
6}
7
8class MinHeap {
9 constructor() {
10 this.heap = [];
11 }
12
13 push(node) {
14 // insert the new node at the end of the heap array
15 this.heap.push(node);
16 // find the correct position for the new node
17 this.bubble_up();
18 }
19
20 bubble_up() {
21 let index = this.heap.length - 1;
22
23 while (index > 0) {
24 const element = this.heap[index];
25 const parentIndex = Math.floor((index - 1) / 2);
26 const parent = this.heap[parentIndex];
27
28 if (parent.priority <= element.priority) break;
29 // if the parent is bigger than the child then swap the parent and child
30 this.heap[index] = parent;
31 this.heap[parentIndex] = element;
32 index = parentIndex;
33 }
34 }
35
36 pop() {
37 const min = this.heap[0];
38 this.heap[0] = this.heap[this.size() - 1];
39 this.heap.pop();
40 this.bubble_down();
41 return min;
42 }
43
44 bubble_down() {
45 let index = 0;
46 let min = index;
47 const n = this.heap.length;
48
49 while (index < n) {
50 const left = 2 * index + 1;
51 const right = left + 1;
52
53 if (left < n && this.heap[left].priority < this.heap[min].priority) {
54 min = left;
55 }
56 if (right < n && this.heap[right].priority < this.heap[min].priority) {
57 min = right;
58 }
59 if (min === index) break;
60 [this.heap[min], this.heap[index]] = [this.heap[index], this.heap[min]];
61 index = min;
62 }
63 }
64
65 peek() {
66 return this.heap[0];
67 }
68
69 size() {
70 return this.heap.length;
71 }
72}
73
74function fun(arr) {
75 const heap = new MinHeap();
76 for (const x of arr) {
77 heap.push(new HeapItem(x));
78 }
79 const res = [];
80 for (let i = 0; i < 3; i++) {
81 res.push(heap.pop().item);
82 }
83 return res;
84}
85
Recommended Readings
LeetCode 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 we
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 algomonster s3 us east 2 amazonaws com recursion jpg You first
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!