LeetCode #219 Easy

Contains Duplicate II

Contains Duplicate II: decide whether the array holds two equal values whose indices differ by at most k. Duplicates further apart than k do not count.

Constraints
  • 1 <= nums.length <= 10⁵
  • -10⁹ <= nums[i] <= 10⁹
  • 0 <= k <= 10⁵
arrayhash tablesliding window
Open on LeetCode ↗
Contains Duplicate II diagramA labelled diagram of the structure this problem turns on.equal values, measured by how far apart their indices are1231idx 0idx 1idx 2idx 3gap = 3 − 0 = 3 ≤ konly the most recent index of a value is worth keeping —an older one is always further away, so it can never win
02

Intuition

Only the most recent occurrence of a value can ever satisfy the distance bound, because any earlier one is strictly further away. So there is no need to remember every position of every value — a map from value to its latest index is enough. On each element, compare against that stored index and update it, which decides the whole question in one pass.

How to spot this pattern

A constraint of the form within k positions turns a global question into a local one, and local questions want either a sliding window or a last-seen map. The tell is a distance bound between two indices rather than a condition on the values themselves.

03

Approach

Try it first

Before reading on: convince yourself that storing only the most recent index of each value never loses a valid answer. Then check what happens if you skip the overwrite when a duplicate is found but is too far away.

1

Why the latest occurrence is the only one worth keeping

Suppose value v appeared at indices 2 and 7, and you now stand at index 9. The gap to 7 is 2 and the gap to 2 is 7 — the earlier occurrence is always the worse candidate. Formally, for a fixed current index i, the distance i - j shrinks as j grows, so the largest stored j minimises it. Any occurrence that fails the test from the most recent position would fail from an older one too, which is what licenses discarding history and storing a single index per value.

2

One pass with a last-seen map

Walk the array holding a dictionary last mapping value to the index where it was most recently seen. At index i with value v, if v is in the map and i - last[v] <= k, a qualifying pair exists and the answer is true immediately. Otherwise write last[v] = i, overwriting any older index. The overwrite is not an optimisation but a correctness step: keeping the stale index would compare future elements against a position that is no longer the nearest one.

3

The sliding-window view and its cost

An equivalent formulation keeps a set of the last k values and asks whether the current element is already inside it, removing the element that falls out of range as the window advances. Both run in O(n) time; the set version caps memory at O(min(n, k)) while the map version can hold up to O(n) distinct values. The map is simpler to write correctly because it needs no eviction step, and eviction is where the window version usually goes wrong — forgetting it lets stale values match across a gap larger than k.

04

Solution & live demo

1class Solution:
2 def containsNearbyDuplicate(self, nums, k):
3 last = {}
4 for i, num in enumerate(nums):
5 if num in last and i - last[num] <= k:
6 return True
7 last[num] = i
8 return False
05

Common pitfalls

Not overwriting the index after a failed distance check

✗ Wrong
if num in last:
    if i - last[num] <= k:
        return True
else:
    last[num] = i
✓ Right
if num in last and i - last[num] <= k:
    return True
last[num] = i

When a duplicate is too far away the stored index must still advance. Leaving the old one means later elements are measured against a stale position and a genuinely close pair is missed — [1,2,1,1] with k = 1 wrongly returns false.

Comparing values rather than index distance

✗ Wrong
if num in last:
    return True
✓ Right
if num in last and i - last[num] <= k:
    return True

This answers Contains Duplicate I instead. It ignores k entirely and returns true for any repeat, however far apart the two occurrences are.

Using a strict inequality on the bound

✗ Wrong
if i - last[num] < k:
✓ Right
if i - last[num] <= k:

The problem says abs(i - j) <= k, so a gap of exactly k qualifies. The strict version rejects the boundary case, failing on [1,0,1] with k = 2.

06

Edge cases

k equals 0

No two distinct indices can differ by at most 0, so the answer is always false.

Duplicates exactly k apart

The bound is inclusive, so i - j == k qualifies and returns true.

Duplicates further than k apart

The test fails, but the index is still overwritten so later pairs are measured correctly.

No duplicates at all

Every value is written once and the loop finishes with false.

Three occurrences, only the last two close enough

Overwriting keeps the nearest index, so the qualifying pair is still found.

07

Complexity

Time
O(n)
Space
O(n)
One pass with constant-time map operations. Space holds at most one entry per distinct value.