You are given an array of integers nums and an integer limit, return the size of the longest non-empty subarray such that the absolute difference between any two elements of this subarray is less than or equal to limit.
Example 1:
Input: nums =[8,2,4,7], limit =4Output:2
Explanation: [8] with maximum absolute diff |8-8| = 0 <= 4. [8,2] with maximum absolute diff |8-2| = 6 > 4. [8,2,4] with maximum absolute diff |8-2| = 6 > 4. [8,2,4,7] with maximum absolute diff |8-2| = 6 > 4. [2] with maximum absolute diff |2-2| = 0 <= 4. [2,4] with maximum absolute diff |2-4| = 2 <= 4. [2,4,7] with maximum absolute diff |2-7| = 5 > 4. [4] with maximum absolute diff |4-4| = 0 <= 4. [4,7] with maximum absolute diff |4-7| = 3 <= 4. [7] with maximum absolute diff |7-7| = 0 <= 4. Therefore, the size of the longest subarray is 2.
Example 2:
Input: nums =[10,1,2,4,7,2], limit =5Output:4
Explanation: The subarray [2,4,7,2] is the longest since the maximum absolute diff is |2-7| = 5 <= 5.
Please upgrade to NeetCode Pro to view company tags.
Prerequisites
Before attempting this problem, you should be comfortable with:
Sliding Window - Expanding and shrinking a window while maintaining validity conditions
Heaps (Priority Queues) - Using max-heap and min-heap to track extreme values in a dynamic set
Monotonic Deques - Maintaining increasing or decreasing order to efficiently track min/max in a sliding window
Balanced BST / Sorted Containers - Using TreeMap, multiset, or SortedDict for ordered element storage
1. Brute Force
Intuition
A subarray is valid if the difference between its maximum and minimum is at most limit. Brute force tries every possible starting index i, then extends the subarray to the right (j) while tracking the current min and max. The moment max - min becomes greater than limit, extending further will only keep it invalid (or worse), so we break and move to the next i.
classSolution:deflongestSubarray(self, nums: List[int], limit:int)->int:
n =len(nums)
res =1for i inrange(n):
mini = maxi = nums[i]for j inrange(i +1, n):
mini =min(mini, nums[j])
maxi =max(maxi, nums[j])if maxi - mini > limit:break
res =max(res, j - i +1)return res
publicclassSolution{publicintlongestSubarray(int[] nums,int limit){int n = nums.length;int res =1;for(int i =0; i < n; i++){int mini = nums[i], maxi = nums[i];for(int j = i +1; j < n; j++){
mini =Math.min(mini, nums[j]);
maxi =Math.max(maxi, nums[j]);if(maxi - mini > limit){break;}
res =Math.max(res, j - i +1);}}return res;}}
classSolution{public:intlongestSubarray(vector<int>& nums,int limit){int n = nums.size();int res =1;for(int i =0; i < n; i++){int mini = nums[i], maxi = nums[i];for(int j = i +1; j < n; j++){
mini =min(mini, nums[j]);
maxi =max(maxi, nums[j]);if(maxi - mini > limit){break;}
res =max(res, j - i +1);}}return res;}};
classSolution{/**
* @param {number[]} nums
* @param {number} limit
* @return {number}
*/longestSubarray(nums, limit){const n = nums.length;let res =1;for(let i =0; i < n; i++){let mini = nums[i],
maxi = nums[i];for(let j = i +1; j < n; j++){
mini = Math.min(mini, nums[j]);
maxi = Math.max(maxi, nums[j]);if(maxi - mini > limit){break;}
res = Math.max(res, j - i +1);}}return res;}}
publicclassSolution{publicintLongestSubarray(int[] nums,int limit){int n = nums.Length;int res =1;for(int i =0; i < n; i++){int mini = nums[i], maxi = nums[i];for(int j = i +1; j < n; j++){
mini = Math.Min(mini, nums[j]);
maxi = Math.Max(maxi, nums[j]);if(maxi - mini > limit){break;}
res = Math.Max(res, j - i +1);}}return res;}}
funclongestSubarray(nums []int, limit int)int{
n :=len(nums)
res :=1for i :=0; i < n; i++{
mini, maxi := nums[i], nums[i]for j := i +1; j < n; j++{if nums[j]< mini {
mini = nums[j]}if nums[j]> maxi {
maxi = nums[j]}if maxi-mini > limit {break}if j-i+1> res {
res = j - i +1}}}return res
}
class Solution {funlongestSubarray(nums: IntArray, limit: Int): Int {val n = nums.size
var res =1for(i in0 until n){var mini = nums[i]var maxi = nums[i]for(j in i +1 until n){
mini =minOf(mini, nums[j])
maxi =maxOf(maxi, nums[j])if(maxi - mini > limit){break}
res =maxOf(res, j - i +1)}}return res
}}
classSolution{funclongestSubarray(_ nums:[Int],_ limit:Int)->Int{let n = nums.count
var res =1for i in0..<n {var mini = nums[i]var maxi = nums[i]for j in(i +1)..<n {
mini =min(mini, nums[j])
maxi =max(maxi, nums[j])if maxi - mini > limit {break}
res =max(res, j - i +1)}}return res
}}
Time & Space Complexity
Time complexity: O(n2)
Space complexity: O(1)
2. Heap
Intuition
We want to find the longest continuous subarray where the difference between the maximum and minimum elements is less than or equal to the given limit.
A simple way would be to check all subarrays, but that would be too slow. Instead, we can use a sliding window approach where we expand the window to the right and shrink it from the left only when the condition becomes invalid.
The key challenge is to quickly know the maximum and minimum values in the current window. To handle this efficiently, we use:
a max heap to track the maximum value
a min heap to track the minimum value
Each heap also stores indices so we can remove elements that move out of the window.
Algorithm
Initialize two heaps:
a max heap to store values (as negative for max behavior) along with their indices
a min heap to store values along with their indices
Use two pointers:
i for expanding the window to the right
j for shrinking the window from the left
For each element at index i:
Add it to both the max heap and the min heap
While the difference between the current maximum and minimum exceeds the limit:
Move the left pointer j forward
Remove elements from both heaps whose indices are less than j (they are outside the window)
After the window becomes valid again:
Update the result with the current window length i - j + 1
Continue this process until all elements are processed
classSolution:deflongestSubarray(self, nums: List[int], limit:int)->int:
maxHeap =[]
minHeap =[]
j =0
res =0for i, v inenumerate(nums):
heapq.heappush(maxHeap,(-v, i))
heapq.heappush(minHeap,(v, i))while-maxHeap[0][0]- minHeap[0][0]> limit:
j +=1while maxHeap and maxHeap[0][1]< j:
heapq.heappop(maxHeap)while minHeap and minHeap[0][1]< j:
heapq.heappop(minHeap)
res =max(res, i - j +1)return res
publicclassSolution{publicintlongestSubarray(int[] nums,int limit){PriorityQueue<int[]> maxHeap =newPriorityQueue<>((a,b)-> b[0]- a[0]);PriorityQueue<int[]> minHeap =newPriorityQueue<>((a,b)-> a[0]- b[0]);int j =0, res =0;for(int i =0; i < nums.length;++i){int v = nums[i];
maxHeap.offer(newint[]{v, i});
minHeap.offer(newint[]{v, i});while(maxHeap.peek()[0]- minHeap.peek()[0]> limit){++j;while(!maxHeap.isEmpty()&& maxHeap.peek()[1]< j){
maxHeap.poll();}while(!minHeap.isEmpty()&& minHeap.peek()[1]< j){
minHeap.poll();}}
res =Math.max(res, i - j +1);}return res;}}
classSolution{public:intlongestSubarray(vector<int>& nums,int limit){
priority_queue<pair<int,int>> maxHeap;
priority_queue<
pair<int,int>,
vector<pair<int,int>>,
greater<pair<int,int>>> minHeap;int j =0, res =0;for(int i =0; i <(int)nums.size();++i){int v = nums[i];
maxHeap.emplace(v, i);
minHeap.emplace(v, i);while(maxHeap.top().first - minHeap.top().first > limit){++j;while(!maxHeap.empty()&& maxHeap.top().second < j)
maxHeap.pop();while(!minHeap.empty()&& minHeap.top().second < j)
minHeap.pop();}
res =max(res, i - j +1);}return res;}};
classSolution{/**
* @param {number[]} nums
* @param {number} limit
* @return {number}
*/longestSubarray(nums, limit){const maxHeap =newPriorityQueue((a, b)=> b[0]- a[0]);const minHeap =newPriorityQueue((a, b)=> a[0]- b[0]);let j =0,
res =0;for(let i =0; i < nums.length;++i){const v = nums[i];
maxHeap.push([v, i]);
minHeap.push([v, i]);while(maxHeap.front()[0]- minHeap.front()[0]> limit){++j;while(!maxHeap.isEmpty()&& maxHeap.front()[1]< j)
maxHeap.pop();while(!minHeap.isEmpty()&& minHeap.front()[1]< j)
minHeap.pop();}
res = Math.max(res, i - j +1);}return res;}}
publicclassSolution{publicintLongestSubarray(int[] nums,int limit){var maxHeap =newPriorityQueue<int[],int>();var minHeap =newPriorityQueue<int[],int>();int j =0, res =0;for(int i =0; i < nums.Length;++i){int v = nums[i];
maxHeap.Enqueue(new[]{v, i},-v);
minHeap.Enqueue(new[]{v, i}, v);while(maxHeap.Peek()[0]- minHeap.Peek()[0]> limit){++j;while(maxHeap.Count >0&& maxHeap.Peek()[1]< j)
maxHeap.Dequeue();while(minHeap.Count >0&& minHeap.Peek()[1]< j)
minHeap.Dequeue();}
res = Math.Max(res, i - j +1);}return res;}}
funclongestSubarray(nums []int, limit int)int{
maxHeap :=&MaxHeap{}
minHeap :=&MinHeap{}
heap.Init(maxHeap)
heap.Init(minHeap)
j, res :=0,0for i, v :=range nums {
heap.Push(maxHeap,[]int{v, i})
heap.Push(minHeap,[]int{v, i})for(*maxHeap)[0][0]-(*minHeap)[0][0]> limit {
j++for maxHeap.Len()>0&&(*maxHeap)[0][1]< j {
heap.Pop(maxHeap)}for minHeap.Len()>0&&(*minHeap)[0][1]< j {
heap.Pop(minHeap)}}if i-j+1> res {
res = i - j +1}}return res
}type MaxHeap [][]intfunc(h MaxHeap)Len()int{returnlen(h)}func(h MaxHeap)Less(i, j int)bool{return h[i][0]> h[j][0]}func(h MaxHeap)Swap(i, j int){ h[i], h[j]= h[j], h[i]}func(h *MaxHeap)Push(x any){*h =append(*h, x.([]int))}func(h *MaxHeap)Pop() any {
old :=*h
n :=len(old)
x := old[n-1]*h = old[0: n-1]return x
}type MinHeap [][]intfunc(h MinHeap)Len()int{returnlen(h)}func(h MinHeap)Less(i, j int)bool{return h[i][0]< h[j][0]}func(h MinHeap)Swap(i, j int){ h[i], h[j]= h[j], h[i]}func(h *MinHeap)Push(x any){*h =append(*h, x.([]int))}func(h *MinHeap)Pop() any {
old :=*h
n :=len(old)
x := old[n-1]*h = old[0: n-1]return x
}
class Solution {funlongestSubarray(nums: IntArray, limit: Int): Int {val maxHeap = PriorityQueue<IntArray>{ a, b -> b[0]- a[0]}val minHeap = PriorityQueue<IntArray>{ a, b -> a[0]- b[0]}var j =0var res =0for(i in nums.indices){val v = nums[i]
maxHeap.offer(intArrayOf(v, i))
minHeap.offer(intArrayOf(v, i))while(maxHeap.peek()[0]- minHeap.peek()[0]> limit){
j++while(maxHeap.isNotEmpty()&& maxHeap.peek()[1]< j){
maxHeap.poll()}while(minHeap.isNotEmpty()&& minHeap.peek()[1]< j){
minHeap.poll()}}
res =maxOf(res, i - j +1)}return res
}}
classSolution{funclongestSubarray(_ nums:[Int],_ limit:Int)->Int{var maxHeap =[(Int,Int)]()var minHeap =[(Int,Int)]()var j =0var res =0for i in0..<nums.count {let v = nums[i]
maxHeap.append((v, i))
maxHeap.sort {$0.0>$1.0}
minHeap.append((v, i))
minHeap.sort {$0.0<$1.0}while maxHeap[0].0- minHeap[0].0> limit {
j +=1while!maxHeap.isEmpty && maxHeap[0].1< j {
maxHeap.removeFirst()}while!minHeap.isEmpty && minHeap[0].1< j {
minHeap.removeFirst()}}
res =max(res, i - j +1)}return res
}}
Time & Space Complexity
Time complexity: O(nlogn)
Space complexity: O(n)
3. Sorted Dict
Intuition
We want the longest continuous subarray where the difference between the maximum and minimum elements is less than or equal to the given limit.
Using a sliding window helps us avoid checking all subarrays. As we expand the window to the right, we need a way to always know the current minimum and maximum values inside the window.
To do this efficiently, we use a sorted data structure that keeps all elements of the current window in order. This allows us to:
get the minimum element from the beginning
get the maximum element from the end
If the difference between these two values becomes greater than the limit, we shrink the window from the left until it becomes valid again.
Algorithm
Initialize a sorted dictionary to store elements of the current window along with their frequencies.
Use two pointers:
l as the left boundary of the window
r as the right boundary of the window
For each element at index r:
Insert it into the sorted dictionary and increase its count
While the difference between the largest and smallest keys in the sorted dictionary exceeds the limit:
Remove the element at index l from the dictionary
Decrease its count and delete it if the count becomes zero
Move the left pointer l forward
Once the window is valid:
Update the result with the current window size r - l + 1
classSolution:deflongestSubarray(self, nums: List[int], limit:int)->int:
tree = SortedDict()
l = res =0for r, x inenumerate(nums):
tree[x]= tree.get(x,0)+1while tree.peekitem(-1)[0]- tree.peekitem(0)[0]> limit:
y = nums[l]
tree[y]-=1if tree[y]==0:del tree[y]
l +=1
res =max(res, r - l +1)return res
publicclassSolution{publicintlongestSubarray(int[] nums,int limit){TreeMap<Integer,Integer> map =newTreeMap<>();int l =0, res =0;for(int r =0; r < nums.length; r++){
map.put(nums[r], map.getOrDefault(nums[r],0)+1);while(map.lastKey()- map.firstKey()> limit){int cnt = map.get(nums[l]);if(cnt ==1) map.remove(nums[l]);else map.put(nums[l], cnt -1);
l++;}
res =Math.max(res, r - l +1);}return res;}}
classSolution{public:intlongestSubarray(vector<int>& nums,int limit){
multiset<int> ms;int l =0, res =0;for(int r =0; r < nums.size(); r++){
ms.insert(nums[r]);while(*ms.rbegin()-*ms.begin()> limit){
ms.erase(ms.find(nums[l]));
l++;}
res =max(res, r - l +1);}return res;}};
publicclassSolution{publicintLongestSubarray(int[] nums,int limit){var map =newSortedList<int,int>();int l =0, res =0;for(int r =0; r < nums.Length; r++){int x = nums[r];if(!map.ContainsKey(x)) map[x]=0;
map[x]++;while(map.Keys[map.Count -1]- map.Keys[0]> limit){int y = nums[l++];
map[y]--;if(map[y]==0) map.Remove(y);}
res = Math.Max(res, r - l +1);}return res;}}
funclongestSubarray(nums []int, limit int)int{
tree := redblacktree.NewWithIntComparator()
l, res :=0,0for r :=0; r <len(nums); r++{
x := nums[r]if val, found := tree.Get(x); found {
tree.Put(x, val.(int)+1)}else{
tree.Put(x,1)}for tree.Size()>0{
maxKey := tree.Right().Key.(int)
minKey := tree.Left().Key.(int)if maxKey-minKey <= limit {break}
y := nums[l]
l++
val,_:= tree.Get(y)if val.(int)==1{
tree.Remove(y)}else{
tree.Put(y, val.(int)-1)}}if r-l+1> res {
res = r - l +1}}return res
}
class Solution {funlongestSubarray(nums: IntArray, limit: Int): Int {val map = TreeMap<Int, Int>()var l =0var res =0for(r in nums.indices){
map[nums[r]]= map.getOrDefault(nums[r],0)+1while(map.lastKey()- map.firstKey()> limit){val cnt = map[nums[l]]!!if(cnt ==1) map.remove(nums[l])else map[nums[l]]= cnt -1
l++}
res =maxOf(res, r - l +1)}return res
}}
classSolution{funclongestSubarray(_ nums:[Int],_ limit:Int)->Int{var map =[Int:Int]()var sortedKeys =[Int]()var l =0var res =0for r in0..<nums.count {let x = nums[r]if map[x]==nil{
sortedKeys.append(x)
sortedKeys.sort()}
map[x,default:0]+=1whilelet maxKey = sortedKeys.last,let minKey = sortedKeys.first,
maxKey - minKey > limit {let y = nums[l]
l +=1
map[y]!-=1if map[y]==0{
map.removeValue(forKey: y)iflet idx = sortedKeys.firstIndex(of: y){
sortedKeys.remove(at: idx)}}}
res =max(res, r - l +1)}return res
}}
Time & Space Complexity
Time complexity: O(nlogn)
Space complexity: O(n)
4. Deque - I
Intuition
We want to find the longest continuous subarray where the difference between the maximum and minimum elements does not exceed the given limit.
A sliding window is a natural choice here, but the main challenge is efficiently tracking the current minimum and maximum in the window as it moves.
To solve this, we use two monotonic deques:
a monotonically increasing deque to keep track of the minimum values
a monotonically decreasing deque to keep track of the maximum values
These deques are maintained in such a way that their front elements always represent the minimum and maximum of the current window.
Algorithm
Initialize two deques:
min_q to store elements in increasing order (front is the minimum)
max_q to store elements in decreasing order (front is the maximum)
Use two pointers:
l for the left boundary of the window
r for expanding the window to the right
For each index r:
Remove elements from the back of min_q while the current value is smaller
Remove elements from the back of max_q while the current value is larger
Add the current value to both deques
While the difference between the front of max_q and min_q exceeds the limit:
If the element leaving the window equals the front of max_q, remove it
If the element leaving the window equals the front of min_q, remove it
Move the left pointer l forward
After the window becomes valid:
Update the result using the current window size r - l + 1
classSolution:deflongestSubarray(self, nums: List[int], limit:int)->int:
min_q = deque()# mono increasing
max_q = deque()# mono decreasing
l = res =0for r inrange(len(nums)):while min_q and nums[r]< min_q[-1]:
min_q.pop()while max_q and nums[r]> max_q[-1]:
max_q.pop()
min_q.append(nums[r])
max_q.append(nums[r])while max_q[0]- min_q[0]> limit:if nums[l]== max_q[0]:
max_q.popleft()if nums[l]== min_q[0]:
min_q.popleft()
l +=1
res =max(res, r - l +1)return res
publicclassSolution{publicintlongestSubarray(int[] nums,int limit){Deque<Integer> minQ =newArrayDeque<>();Deque<Integer> maxQ =newArrayDeque<>();int l =0, res =0;for(int r =0; r < nums.length; r++){while(!minQ.isEmpty()&& nums[r]< minQ.peekLast()){
minQ.removeLast();}while(!maxQ.isEmpty()&& nums[r]> maxQ.peekLast()){
maxQ.removeLast();}
minQ.addLast(nums[r]);
maxQ.addLast(nums[r]);while(maxQ.peekFirst()- minQ.peekFirst()> limit){if(nums[l]== maxQ.peekFirst()){
maxQ.removeFirst();}if(nums[l]== minQ.peekFirst()){
minQ.removeFirst();}
l++;}
res =Math.max(res, r - l +1);}return res;}}
classSolution{public:intlongestSubarray(vector<int>& nums,int limit){
deque<int> minQ, maxQ;int l =0, res =0;for(int r =0; r < nums.size(); r++){while(!minQ.empty()&& nums[r]< minQ.back()){
minQ.pop_back();}while(!maxQ.empty()&& nums[r]> maxQ.back()){
maxQ.pop_back();}
minQ.push_back(nums[r]);
maxQ.push_back(nums[r]);while(maxQ.front()- minQ.front()> limit){if(nums[l]== maxQ.front()){
maxQ.pop_front();}if(nums[l]== minQ.front()){
minQ.pop_front();}
l++;}
res =max(res, r - l +1);}return res;}};
classSolution{/**
* @param {number[]} nums
* @param {number} limit
* @return {number}
*/longestSubarray(nums, limit){const minQ =newDeque();const maxQ =newDeque();let l =0,
res =0;for(let r =0; r < nums.length; r++){while(!minQ.isEmpty()&& nums[r]< minQ.back()){
minQ.popBack();}while(!maxQ.isEmpty()&& nums[r]> maxQ.back()){
maxQ.popBack();}
minQ.pushBack(nums[r]);
maxQ.pushBack(nums[r]);while(maxQ.front()- minQ.front()> limit){if(nums[l]=== maxQ.front()){
maxQ.popFront();}if(nums[l]=== minQ.front()){
minQ.popFront();}
l++;}
res = Math.max(res, r - l +1);}return res;}}
publicclassSolution{publicintLongestSubarray(int[] nums,int limit){var minQ =newLinkedList<int>();var maxQ =newLinkedList<int>();int l =0, res =0;for(int r =0; r < nums.Length; r++){while(minQ.Count >0&& nums[r]< minQ.Last.Value){
minQ.RemoveLast();}while(maxQ.Count >0&& nums[r]> maxQ.Last.Value){
maxQ.RemoveLast();}
minQ.AddLast(nums[r]);
maxQ.AddLast(nums[r]);while(maxQ.First.Value - minQ.First.Value > limit){if(nums[l]== maxQ.First.Value){
maxQ.RemoveFirst();}if(nums[l]== minQ.First.Value){
minQ.RemoveFirst();}
l++;}
res = System.Math.Max(res, r - l +1);}return res;}}
funclongestSubarray(nums []int, limit int)int{
minQ :=[]int{}
maxQ :=[]int{}
l, res :=0,0for r :=0; r <len(nums); r++{forlen(minQ)>0&& nums[r]< minQ[len(minQ)-1]{
minQ = minQ[:len(minQ)-1]}forlen(maxQ)>0&& nums[r]> maxQ[len(maxQ)-1]{
maxQ = maxQ[:len(maxQ)-1]}
minQ =append(minQ, nums[r])
maxQ =append(maxQ, nums[r])for maxQ[0]-minQ[0]> limit {if nums[l]== maxQ[0]{
maxQ = maxQ[1:]}if nums[l]== minQ[0]{
minQ = minQ[1:]}
l++}if r-l+1> res {
res = r - l +1}}return res
}
class Solution {funlongestSubarray(nums: IntArray, limit: Int): Int {val minQ = ArrayDeque<Int>()val maxQ = ArrayDeque<Int>()var l =0var res =0for(r in nums.indices){while(minQ.isNotEmpty()&& nums[r]< minQ.last()){
minQ.removeLast()}while(maxQ.isNotEmpty()&& nums[r]> maxQ.last()){
maxQ.removeLast()}
minQ.addLast(nums[r])
maxQ.addLast(nums[r])while(maxQ.first()- minQ.first()> limit){if(nums[l]== maxQ.first()){
maxQ.removeFirst()}if(nums[l]== minQ.first()){
minQ.removeFirst()}
l++}
res =maxOf(res, r - l +1)}return res
}}
classSolution{funclongestSubarray(_ nums:[Int],_ limit:Int)->Int{var minQ =[Int]()var maxQ =[Int]()var l =0var res =0for r in0..<nums.count {while!minQ.isEmpty && nums[r]< minQ.last!{
minQ.removeLast()}while!maxQ.isEmpty && nums[r]> maxQ.last!{
maxQ.removeLast()}
minQ.append(nums[r])
maxQ.append(nums[r])while maxQ.first!- minQ.first!> limit {if nums[l]== maxQ.first!{
maxQ.removeFirst()}if nums[l]== minQ.first!{
minQ.removeFirst()}
l +=1}
res =max(res, r - l +1)}return res
}}
Time & Space Complexity
Time complexity: O(n)
Space complexity: O(n)
5. Deque - II
Intuition
We want the longest continuous subarray where the difference between the maximum and minimum values is at most limit.
A sliding window works well because the subarray must be continuous. As we expand the window to the right, we need to always know the current minimum and maximum in the window.
To track them efficiently, we maintain two monotonic deques:
inc (increasing deque): keeps possible minimum values in increasing order, so the front is the current minimum
dec (decreasing deque): keeps possible maximum values in decreasing order, so the front is the current maximum
Whenever the window becomes invalid (max - min > limit), we shrink it from the left by moving j forward and removing the left element from the deques if it matches their front.
Algorithm
Initialize two deques using the first element:
inc for minimum tracking (monotonic increasing)
dec for maximum tracking (monotonic decreasing)
Use two pointers:
i expands the window to the right
j shrinks the window from the left when needed
For each new element nums[i]:
Maintain inc by popping from the back while the back is greater than nums[i]
Maintain dec by popping from the back while the back is less than nums[i]
Append nums[i] to both deques
If the window becomes invalid (dec[0] - inc[0] > limit):
If the element leaving the window (nums[j]) equals the front of dec, pop it from dec
If it equals the front of inc, pop it from inc
Move j forward by 1 to shrink the window
After processing all elements, the valid window starts at j and ends at the last index, so its length is len(nums) - j
A common mistake is to use regular queues or simply track a single max and min value. When the window shrinks, you lose track of the next maximum or minimum. Monotonic deques maintain candidates in order so that the front always gives the current extreme value for the window.
Removing the Wrong Element When Shrinking the Window
When the left pointer moves, you must only remove elements from the deques if they match the element leaving the window. A subtle bug occurs when you pop from the deque unconditionally, which can remove elements that are still inside the valid window.
Not Maintaining Deque Invariants Correctly
For the min deque to work correctly, it must remain monotonically increasing from front to back. For the max deque, it must remain monotonically decreasing. Forgetting to pop elements that violate these invariants before adding a new element leads to incorrect min/max values.
Comparing Indices Instead of Values
When using deques that store indices instead of values, a common mistake is to compare indices when you should compare values, or vice versa. Be consistent about what the deque stores and ensure comparisons use the appropriate array lookups.
Off-by-One Errors in Window Size Calculation
The window size is right - left + 1, not right - left. Forgetting the +1 when updating the result will undercount the longest valid subarray by one element.