Loading problems...
TL;DR: Use a sliding window to maintain a range of indices where the difference between the maximum and minimum values in the window never exceeds 2, adding to the total count at each step.
The LeetCode 2762: Continuous Subarrays problem asks us to count the total number of subarrays where the absolute difference between any two elements is at most 2. Formally, for every pair of indices in the subarray, we must satisfy . This condition is mathematically equivalent to checking if the difference between the maximum and minimum elements in the subarray is less than or equal to 2.
The naive approach involves generating every possible subarray and checking the validity condition for each one.
i from 0 to n-1.j from i to n-1.nums[i...j], scan its elements to find the minimum and maximum values.max_val - min_val <= 2, increment the counter.python# Pseudo-code for Brute Force count = 0 for i in range(len(nums)): for j in range(i, len(nums)): sub = nums[i : j+1] if max(sub) - min(sub) <= 2: count += 1 return count
Time Complexity Analysis: This approach requires three nested loops (two for the boundaries, one implied for finding min/max). This results in a time complexity of . Even if we optimize the min/max finding to run incrementally, it remains . Given the constraint , an solution requires roughly operations, which will inevitably result in a Time Limit Exceeded (TLE) error.
The key intuition is that if a subarray ending at index R starting at index L (i.e., nums[L...R]) is valid, then every subarray ending at R contained within it (e.g., nums[L+1...R], nums[L+2...R]) is also valid.
Conversely, if adding a new element at R breaks the condition (making max - min > 2), we must shrink the window from the left (L) until the condition is restored. We do not need to reset R; we can simply slide L forward.
The fundamental invariant we must maintain is:
To implement this efficiently, we need a data structure that allows us to:
A Sorted Map (TreeMap in Java/C++) or a Hash Map (since the value range is very small) works perfectly here.
Visual Description:
Imagine a window expanding to the right. As R moves to R+1, we include a new number. If this new number causes the difference between the largest and smallest numbers in our window to exceed 2, the window is "broken." We fix it by moving L to the right, ejecting numbers from the left side until the range of values in the window tightens back to . Once valid, the number of valid subarrays ending at R is simply the window's length, .

Let's trace nums = [5, 4, 2, 4].
L=0, R=0, Map={}.Map={5:1}. Min=5, Max=5. Diff=0 (Valid).Map={4:1, 5:1}. Min=4, Max=5. Diff=1 (Valid).Map={2:1, 4:1, 5:1}. Min=2, Max=5. Diff=3 (Invalid).nums[L] (5). L becomes 1. Map={2:1, 4:1}. Min=2, Max=4. Diff=2 (Valid).Map={2:1, 4:2}. Min=2, Max=4. Diff=2 (Valid).Final Output: 8.
The algorithm relies on the property that if nums[L...R] is a continuous subarray, then nums[k...R] is also continuous for all . By finding the smallest such that nums[L...R] is valid (the widest possible window ending at ), we guarantee that we count all possible valid start points for the current end point . The sliding window invariant ensures that we never count an invalid subarray and never miss a valid one because only moves forward when strictly necessary.
In C++, std::map is ordered, making begin() (min) and rbegin() (max) access efficient.
cppclass Solution { public: long long continuousSubarrays(vector<int>& nums) { long long count = 0; int left = 0; // Map stores value -> frequency // Ordered map keeps keys sorted, allowing O(1) access to min/max // given the small constraint on range size. map<int, int> freq; for (int right = 0; right < nums.size(); ++right) { freq[nums[right]]++; // While the condition is broken (max - min > 2) // freq.rbegin()->first accesses the largest key // freq.begin()->first accesses the smallest key while (freq.rbegin()->first - freq.begin()->first > 2) { freq[nums[left]]--; if (freq[nums[left]] == 0) { freq.erase(nums[left]); } left++; } // Add the number of valid subarrays ending at 'right' count += (right - left + 1); } return count; } };
Java's TreeMap provides efficient access to the first (lowest) and last (highest) keys.
javaimport java.util.TreeMap; class Solution { public long continuousSubarrays(int[] nums) { long count = 0; int left = 0; // TreeMap keeps keys sorted naturally TreeMap<Integer, Integer> map = new TreeMap<>(); for (int right = 0; right < nums.length; right++) { map.put(nums[right], map.getOrDefault(nums[right], 0) + 1); // Check validity: max - min > 2 while (map.lastKey() - map.firstKey() > 2) { map.put(nums[left], map.get(nums[left]) - 1); if (map.get(nums[left]) == 0) { map.remove(nums[left]); } left++; } // All subarrays ending at 'right' starting from 'left' to 'right' are valid count += (right - left + 1); } return count; } }
In Python, we can use a standard dictionary. Since the window size (in terms of unique values) is constrained to be very small (max diff 2 means 3 keys), min() and max() on the dictionary keys are effectively .
pythonfrom collections import defaultdict class Solution: def continuousSubarrays(self, nums: List[int]) -> int: count = 0 left = 0 freq = defaultdict(int) for right, num in enumerate(nums): freq[num] += 1 # Check condition. Since valid range is small, # finding max/min of keys is effectively O(1). while max(freq) - min(freq) > 2: freq[nums[left]] -= 1 if freq[nums[left]] == 0: del freq[nums[left]] left += 1 # Add number of valid subarrays ending at current index count += (right - left + 1) return count
Time Complexity: Although we use a Map (TreeMap or Hash Map), the number of distinct elements in the map at any valid state is extremely small (at most 3, e.g., ). In invalid states, it is at most 4. Therefore, map operations (insertion, deletion, finding min/max) are effectively constant time operations relative to . We iterate times, leading to linear complexity.
Space Complexity: Similar to the time complexity reasoning, the map stores at most a constant number of keys (roughly 3 to 4) regardless of the input size . Thus, the auxiliary space is constant.
The Sliding Window - Variable Size pattern is versatile. Understanding how to expand right and shrink left based on a condition applies directly to these problems:
Why does my brute force solution get Time Limit Exceeded (TLE)?
In all these cases, the logic add(right) -> while(invalid) remove(left) -> update_answer remains the same.
Why is my logic failing when elements are removed?
min() and max() functions will still see the key even if its value is 0, leading to incorrect validation logic.Why can't I just track min_val and max_val as simple variables?
min and max using simple integers instead of a data structure.max_val is removed from the left, you don't know what the next largest value in the window is without scanning or using a sorted structure/heap.