15% off all plans & roadmaps Use code
See plansEditorial
Core insight
Contains Duplicate II · Sliding Window
Core Insight for Contains Duplicate II
The brute force approach is inefficient because it repeatedly scans the same elements to check for duplicates. For every index , we look ahead at the next elements. When we move to , we look at almost the same set of elements again.
The key insight is to maintain a "memory" of the elements currently within the valid distance . As we iterate through the array with a pointer right, we are effectively considering a window ending at right.
To satisfy the condition , we only care about elements in the range [right - k, right - 1]. By maintaining a Hash Set representing this sliding window, we can check if nums[right] exists in the set in time.
The pattern enforces a strict constraint: The window size must not exceed .
- As we expand
right, we add elements to our window (Hash Set). - If the window size exceeds (i.e., the distance between
leftandrightis greater than ), we shrink the window from theleftby removingnums[left]from the set. - This ensures that at any point, the set only contains numbers from the previous indices.
Visual Description: Imagine a window frame that can hold at most numbers sliding across the array from left to right. As the frame moves one step forward to include a new number, the oldest number (if the frame is full) drops out of the back. We simply check if the new number entering the frame matches any number currently inside it.
Deep dive
Full breakdown
Full reasoning, pitfalls, and implementation flow
LeetCode 219: Contains Duplicate II Solution & Explanation
Problem Overview
TL;DR: The optimal solution uses a Hash Set to maintain a sliding window of the most recent elements, allowing for duplicate detection.
The LeetCode 219 problem, "Contains Duplicate II," asks us to determine if an integer array nums contains any duplicate elements located within a specific proximity. specifically, we need to return true if there exist two distinct indices and such that nums[i] == nums[j] and the absolute difference between the indices is at most (). If no such pair exists, we return false.
This is a popular interview question because it tests the ability to optimize a search operation within a localized range of an array.
Brute Force Approach for Contains Duplicate II
The naive approach involves checking every possible pair of indices in the array to see if they satisfy both conditions: value equality and index proximity.
We iterate through each element at index and then iterate through subsequent elements at index where . For each pair, we check if nums[i] == nums[j].
Pseudo-code:
for i from 0 to length(nums) - 1:
for j from i + 1 to min(i + k, length(nums) - 1):
if nums[i] == nums[j]:
return true
return falseTime Complexity Analysis: The outer loop runs times. The inner loop runs up to times. In the worst case (where is close to ), this results in a time complexity of or roughly .
Why it fails:
The constraints allow nums.length up to and up to . An solution will perform approximately operations in the worst case, leading to a Time Limit Exceeded (TLE) error. We need a linear time solution.
Algorithm Strategy: Sliding Window Patterns
We utilize the Sliding Window - Variable Size (Condition-Based) strategy combined with a Hash Set for efficient lookups.
- State Management: We maintain a
window(Hash Set) that stores the unique elements found in the current range . - Expansion: We iterate through the array using a
rightpointer (from to ). - Constraint Enforcement: Before processing
nums[right], we check if the current window size violates the condition (distance ). Ifright - left > k(or simply if the window size exceeds ), we remove the element atnums[left]from the set and incrementleft. - Check and Update:
- Check if
nums[right]is already in thewindow. If it is, we have found a duplicate within distance , so we returntrue. - If not, add
nums[right]to thewindow.
- Check if
- Termination: If the loop completes without finding a duplicate, return
false.
Execution Flow
Let's trace the algorithm with nums = [1, 2, 3, 1] and k = 3.
- Initialize:
window = {},left = 0. - Index right = 0:
val = 1- Window size is 0 (valid).
1is not inwindow.- Add
1.window = {1}.
- Index right = 1:
val = 2- Window size is 1 (valid).
2is not inwindow.- Add
2.window = {1, 2}.
- Index right = 2:
val = 3- Window size is 2 (valid).
3is not inwindow.- Add
3.window = {1, 2, 3}.
- Index right = 3:
val = 1- Window size is 3 (valid).
1IS inwindow.- Return True.
Now consider nums = [1, 2, 3, 1, 2, 3] and k = 2.
...
- When
rightreaches index 3 (val = 1):- Current window indices effectively:
[1, 2](values{2, 3}). - The element at index 0 (
1) was removed because index . 1is not in{2, 3}. Add1.
- Current window indices effectively:
- This ensures we only match within distance .
Proof of Correctness
The algorithm relies on the invariant that the Hash Set contains exactly the elements from the subarray nums[max(0, right-k) ... right-1].
- Completeness: If a duplicate pair exists with , then when the algorithm reaches index (
right = j), the index will satisfy . Therefore,nums[i]will still be present in the Hash Set. The checkwindow.contains(nums[right])will correctly returntrue. - Soundness: The algorithm removes
nums[left]as soon asright - left > k. Thus, ifwindow.contains(nums[right])returnstrue, the matching element must be at an index such thatright - i <= k. This satisfies the problem constraints.
Pattern Reuse Notes
The Sliding Window - Variable Size pattern is versatile. Understanding how to maintain window invariants (like "size " or "unique characters") applies directly to:
- LeetCode 3: Longest Substring Without Repeating Characters: Instead of a fixed max size, the window expands until a duplicate is found, then shrinks to restore uniqueness.
- LeetCode 76: Minimum Window Substring: The window expands to satisfy a condition (contain all chars of T) and shrinks to minimize length.
- LeetCode 209: Minimum Size Subarray Sum: The window shrinks from the left as long as the sum condition () is met to find the minimal length.
- LeetCode 424: Longest Repeating Character Replacement: The window grows and shrinks based on the count of the dominant character plus replacements.
Feynman coach · Before you peek at the code
Can you explain the core insight in your own words?
If you can explain it before reading code, you’re far more likely to recall it under interview pressure.
Reference Implementation
C++ Solution for LeetCode 219
1class Solution {
2public:
3 bool containsNearbyDuplicate(vector<int>& nums, int k) {
4 // Hash set to store elements in the current window of size k
5 unordered_set<int> window;
6 int left = 0;
7
8 for (int right = 0; right < nums.size(); ++right) {
9 // If window size exceeds k, shrink from the left
10 if (right - left > k) {
11 window.erase(nums[left]);
12 left++;
13 }
14
15 // Check if current element exists in the window
16 if (window.count(nums[right])) {
17 return true;
18 }
19
20 // Add current element to the window
21 window.insert(nums[right]);
22 }
23
24 return false;
25 }
26};Java Solution for LeetCode 219
1class Solution {
2 public boolean containsNearbyDuplicate(int[] nums, int k) {
3 // Hash set to store elements in the current window of size k
4 Set<Integer> window = new HashSet<>();
5 int left = 0;
6
7 for (int right = 0; right < nums.length; right++) {
8 // If window size exceeds k, shrink from the left
9 if (right - left > k) {
10 window.remove(nums[left]);
11 left++;
12 }
13
14 // Check if current element exists in the window
15 if (window.contains(nums[right])) {
16 return true;
17 }
18
19 // Add current element to the window
20 window.add(nums[right]);
21 }
22
23 return false;
24 }
25}Python Solution for LeetCode 219
1class Solution:
2 def containsNearbyDuplicate(self, nums: List[int], k: int) -> bool:
3 # Hash set to store elements in the current window of size k
4 window = set()
5 left = 0
6
7 for right in range(len(nums)):
8 # If window size exceeds k, shrink from the left
9 if right - left > k:
10 window.remove(nums[left])
11 left += 1
12
13 # Check if current element exists in the window
14 if nums[right] in window:
15 return True
16
17 # Add current element to the window
18 window.add(nums[right])
19
20 return False