Given an array of integers nums
and an integer limit
, your task is to find the length of the longest continuous subarray such that the absolute difference between any two elements in this subarray is less than or equal to limit
.
In other words, for any subarray nums[i..j]
(where i ≤ j
), the following must be true for every pair of indices p
and q
in [i, j]
:
|nums[p] - nums[q]| ≤ limit
Return the maximum possible length of such a subarray. Each element can be used only once per subarray, and you cannot reuse elements across different subarrays.
1 ≤ nums.length ≤ 10^5
1 ≤ nums[i] ≤ 10^9
0 ≤ limit ≤ 10^9
The initial idea might be to check every possible subarray and verify if the absolute difference between any two elements is within the limit
. However, this brute-force approach is inefficient for large arrays, as it would require checking O(N^2)
subarrays, and for each, possibly comparing all pairs, leading to O(N^3)
time complexity.
To optimize, notice that for any subarray, the maximum absolute difference is simply the difference between its maximum and minimum elements. So we just need to ensure that max - min ≤ limit
for the current window.
This insight leads us to use a sliding window approach, where we maintain a window [left, right] and efficiently keep track of the maximum and minimum values within the window. If at any point the difference exceeds the limit
, we shrink the window from the left until the condition is satisfied again.
The challenge is how to efficiently get the min and max values in a moving window. Using data structures like balanced BSTs or double-ended queues (deques) allows us to do this in O(1)
or O(logN)
per operation.
We'll use a sliding window combined with two deques:
left
and right
at 0.right
through the array:
nums[right]
into both deques, maintaining their monotonic properties (pop from back while necessary).max - min > limit
(using the fronts of the deques).left
forward and remove elements from the deques if they're out of the window.This approach ensures that each element is added and removed from each deque at most once, resulting in linear time complexity.
Example: nums = [8, 2, 4, 7]
, limit = 4
The longest valid subarray is [2, 4] or [4, 7], both of length 2. So the answer is 2.
O(N^2)
time, where N
is the length of nums
. Space is O(1)
(not counting input).
O(N)
. Space complexity is O(N)
in the worst case (if all elements are increasing or decreasing).
By recognizing that the maximum absolute difference in a subarray is simply the difference between its maximum and minimum, we can use a sliding window with monotonic deques to efficiently track the longest valid subarray. This approach avoids unnecessary repeated work and scales to large arrays, making it a practical and elegant solution to the problem.
from collections import deque
class Solution:
def longestSubarray(self, nums, limit):
maxDeque = deque()
minDeque = deque()
left = 0
res = 0
for right, num in enumerate(nums):
while maxDeque and num > maxDeque[-1]:
maxDeque.pop()
maxDeque.append(num)
while minDeque and num < minDeque[-1]:
minDeque.pop()
minDeque.append(num)
while maxDeque[0] - minDeque[0] > limit:
if nums[left] == maxDeque[0]:
maxDeque.popleft()
if nums[left] == minDeque[0]:
minDeque.popleft()
left += 1
res = max(res, right - left + 1)
return res
#include <deque>
#include <vector>
#include <algorithm>
using namespace std;
class Solution {
public:
int longestSubarray(vector<int>& nums, int limit) {
deque<int> maxDeque, minDeque;
int left = 0, res = 0;
for (int right = 0; right < nums.size(); ++right) {
while (!maxDeque.empty() && nums[right] > maxDeque.back())
maxDeque.pop_back();
maxDeque.push_back(nums[right]);
while (!minDeque.empty() && nums[right] < minDeque.back())
minDeque.pop_back();
minDeque.push_back(nums[right]);
while (maxDeque.front() - minDeque.front() > limit) {
if (nums[left] == maxDeque.front())
maxDeque.pop_front();
if (nums[left] == minDeque.front())
minDeque.pop_front();
++left;
}
res = max(res, right - left + 1);
}
return res;
}
};
import java.util.*;
class Solution {
public int longestSubarray(int[] nums, int limit) {
Deque<Integer> maxDeque = new LinkedList<>();
Deque<Integer> minDeque = new LinkedList<>();
int left = 0, res = 0;
for (int right = 0; right < nums.length; ++right) {
while (!maxDeque.isEmpty() && nums[right] > maxDeque.getLast())
maxDeque.removeLast();
maxDeque.addLast(nums[right]);
while (!minDeque.isEmpty() && nums[right] < minDeque.getLast())
minDeque.removeLast();
minDeque.addLast(nums[right]);
while (maxDeque.getFirst() - minDeque.getFirst() > limit) {
if (nums[left] == maxDeque.getFirst())
maxDeque.removeFirst();
if (nums[left] == minDeque.getFirst())
minDeque.removeFirst();
left++;
}
res = Math.max(res, right - left + 1);
}
return res;
}
}
/**
* @param {number[]} nums
* @param {number} limit
* @return {number}
*/
var longestSubarray = function(nums, limit) {
let maxDeque = [];
let minDeque = [];
let left = 0, res = 0;
for (let right = 0; right < nums.length; ++right) {
while (maxDeque.length && nums[right] > maxDeque[maxDeque.length - 1])
maxDeque.pop();
maxDeque.push(nums[right]);
while (minDeque.length && nums[right] < minDeque[minDeque.length - 1])
minDeque.pop();
minDeque.push(nums[right]);
while (maxDeque[0] - minDeque[0] > limit) {
if (nums[left] === maxDeque[0]) maxDeque.shift();
if (nums[left] === minDeque[0]) minDeque.shift();
left++;
}
res = Math.max(res, right - left + 1);
}
return res;
};