Contains Duplicate II
Problem
nums and an integer k, decide whether
some value appears at two different indices i and j that are
at most k apart (|i - j| ≤ k).[1,2,3,1], k=3Outputtrue[1,0,1,1], k=1Outputtrue[1,2,3,1,2,3], k=2Outputfalse1 ≤ n ≤ 105 · -109 ≤ nums[i] ≤ 109 · 0 ≤ k ≤ 105
Try it yourself
class Solution {
public boolean containsNearbyDuplicate(int[] nums, int k) {
}
}
i only the previous k elements matter. Keep
exactly those in a hash set window: a hit on insert means a close duplicate.Instinct Need "have I seen this before?", "how many times?" or "group things that look alike"? Trade memory for O(1) lookups with a HashMap, HashSet or int[26].
Memory hack Don't search, remember - every element checks in at the front desk, and the desk answers instantly.
Open the Hashing & Counting guide →Approaches
Brute ForceCheck the Next k Elements
O(n · min(n,k))Space O(1)For every i, compare nums[i] with each
nums[j] for j in (i, i+k]. Any equal pair in
that range is a valid answer.
public boolean containsNearbyDuplicate(int[] nums, int k) { for (int i = 0; i < nums.length; i++) { for (int j = i + 1; j <= i + k && j < nums.length; j++) { if (nums[i] == nums[j]) return true; } } return false; }
No extra memory, but ~1010 comparisons when n and k are large.
BetterHash Map of Last-Seen Index
O(n)Space O(n)Store each value's most recent index. When the value reappears, the
nearest earlier copy is the one in the map, so i - last ≤ k is the
only check needed. Older copies are always farther away.
public boolean containsNearbyDuplicate(int[] nums, int k) { Map<Integer, Integer> last = new HashMap<>(); for (int i = 0; i < nums.length; i++) { Integer prev = last.put(nums[i], i); // returns old index, if any if (prev != null && i - prev <= k) return true; } return false; }
Linear time, but the map keeps every distinct value ever seen.
OptimalSliding Window Hash Set
O(n)Space O(min(n,k))Maintain a set holding exactly the last k values. If
nums[i] is already inside, its twin is ≤ k away. After inserting,
evict nums[i-k] once the set exceeds k. The window holds
no duplicates (we'd have returned), so evicting by value is safe.
public boolean containsNearbyDuplicate(int[] nums, int k) { Set<Integer> window = new HashSet<>(); for (int i = 0; i < nums.length; i++) { if (!window.add(nums[i])) return true; // already in window if (window.size() > k) window.remove(nums[i - k]); } return false; }
Dry run[1,2,3,1,2,3], k=2
- i=11w2i w3123set={1,2}
- i=212w3i w123size 3>2 → drop 1
- i=3123w1i w231 new; drop 2
- i=41231w2i w32 new; drop 3
- i=512312w3i w3 new → false
Linear time with memory bounded by the window size.
Set.add returns false for an existing element —
one call does both the lookup and the insert.At a glance
| Level | Approach | Time | Space |
|---|---|---|---|
| Brute Force | Check the Next k Elements | O(n · min(n,k)) | O(1) |
| Better | Hash Map of Last-Seen Index | O(n) | O(n) |
| Optimal | Sliding Window Hash Set | O(n) | O(min(n,k)) |
- Off-by-one eviction: after inserting, drop
nums[i-k](notnums[i-k-1]). - Using
< kinstead of≤ k— distance exactlykcounts. - Forgetting
k = 0: two different indices can never be 0 apart → always false.
- Clarify: the indices must differ, and the bound is inclusive.
- Mention the map trade-off: simpler but O(n) space vs. O(k) for the window.
- Bridge to Contains Duplicate III: swap the set for a
TreeSetor buckets.