File: contains-duplicate-ii/solution.py

Date: 2026-06-06

Time: 15:50

Contains Duplicate II — Solution Explanation

Purpose

This file solves LeetCode 219: Contains Duplicate II. It determines whether an array contains two distinct indices i and j such that nums[i] == nums[j] and abs(i - j) <= k. It owns the single Solution.containsNearbyDuplicate method, which is the standard LeetCode entry point.

Key Components

Solution.containsNearbyDuplicate(nums, k) -> bool

Patterns

Hash map for O(1) lookups. Rather than a brute-force O(n*k) nested scan, this uses a dictionary to check in constant time whether a value was seen recently. This is the canonical "value → last index" pattern seen across many LeetCode sliding-window and duplicate-detection problems (compare with two-sum/solution.py).

Single-pass greedy update. The dictionary always stores the *most recent* index for each value, not the first. This is correct because if a value at index j is too far from its earlier occurrence at index i, any future occurrence at index m > j will be closer to j than to i. Storing only the latest index is sufficient — you never need to check against older occurrences.

Early return. The function short-circuits on the first duplicate found within range, avoiding unnecessary iteration.

Dependencies

Flow

1. Initialize empty dict last_seen.

2. Iterate over nums with index i and value num.

3. If num exists in lastseen AND i - lastseen[num] <= k, return True.

4. Unconditionally update last_seen[num] = i (overwriting any previous index).

5. If the loop completes without finding a match, return False.

The subtlety is in step 4: the update happens *after* the check, and it happens regardless of whether the check passed. This means the dict always reflects the latest index, which is the optimal position to measure future distances from.

Invariants

Error Handling

None. The function assumes valid inputs per LeetCode constraints. An empty nums list or k=0 (only self-match, which can't happen with distinct indices) are handled implicitly — the loop either doesn't execute or the distance check always fails.

Complexity

An alternative approach uses a sliding-window *set* of size k, evicting the oldest element when the window exceeds k. That caps space at O(k) but is slightly more code. This solution trades a potentially larger dict for simpler logic.

Topics to Explore

Beliefs