A clear explanation of detecting whether equal values appear within distance k using a hash map or sliding window set.
Problem Restatement
We are given an integer array nums and an integer k.
We need to return True if there are two different indices i and j such that:
nums[i] == nums[j]and:
abs(i - j) <= kOtherwise, return False.
The official statement asks whether two distinct indices have equal values and index distance at most k. Constraints include 1 <= nums.length <= 10^5, -10^9 <= nums[i] <= 10^9, and 0 <= k <= 10^5.
Input and Output
| Item | Meaning |
|---|---|
| Input | Integer array nums and integer k |
| Output | Boolean |
True | Duplicate values exist within distance k |
False | No such pair exists |
| Distinct indices | i and j must be different |
Example function shape:
def containsNearbyDuplicate(nums: list[int], k: int) -> bool:
...Examples
Example 1:
nums = [1, 2, 3, 1]
k = 3The value 1 appears at indices 0 and 3.
Distance:
abs(0 - 3) = 3Since 3 <= k, the answer is:
TrueExample 2:
nums = [1, 0, 1, 1]
k = 1The value 1 appears at indices 2 and 3.
Distance:
abs(2 - 3) = 1Answer:
TrueExample 3:
nums = [1, 2, 3, 1, 2, 3]
k = 2Duplicates exist, but none are close enough.
The repeated 1s are at indices 0 and 3.
Distance:
3But 3 > 2.
Answer:
FalseFirst Thought: Check Every Pair
The direct method is to compare every pair of indices.
class Solution:
def containsNearbyDuplicate(self, nums: list[int], k: int) -> bool:
n = len(nums)
for i in range(n):
for j in range(i + 1, n):
if nums[i] == nums[j] and j - i <= k:
return True
return FalseThis is correct, but too slow.
Problem With Brute Force
The array can contain up to 10^5 elements.
Checking every pair may take:
O(n^2)That is too large.
We need to remember where each value last appeared.
Key Insight: Store the Last Index
When scanning from left to right, suppose the current index is i.
If we have seen nums[i] before at index j, then the nearest previous duplicate is the most recent index for that value.
So we store:
value -> latest indexWhen we see the same value again, check:
i - previous_index <= kIf yes, return True.
If no, update the latest index.
Why update?
Because the latest occurrence gives the best chance of being close to a future index.
Algorithm
- Create an empty dictionary
last_seen. - Scan the array with index
iand valuenum. - If
numexists inlast_seen:- check whether
i - last_seen[num] <= k - if yes, return
True
- check whether
- Set
last_seen[num] = i. - If the scan finishes, return
False.
Walkthrough
Use:
nums = [1, 2, 3, 1]
k = 3Start:
last_seen = {}Process index 0, value 1:
last_seen = {1: 0}Process index 1, value 2:
last_seen = {1: 0, 2: 1}Process index 2, value 3:
last_seen = {1: 0, 2: 1, 3: 2}Process index 3, value 1.
Previous index of 1 is 0.
Distance:
3 - 0 = 3Since 3 <= k, return:
TrueCorrectness
The dictionary last_seen stores the latest index where each value appeared before the current index.
When the algorithm processes nums[i], any valid pair ending at i must use a previous index where the same value appeared.
Among all previous occurrences of the same value, the latest one has the smallest distance to i.
Therefore, if the latest previous occurrence is farther than k, then all earlier occurrences are also farther than k.
If the latest previous occurrence is within distance k, the algorithm returns True, which is correct because it has found two distinct indices with equal values and distance at most k.
If the algorithm finishes without returning True, then no index has a previous equal value close enough, so no valid pair exists.
Thus the algorithm is correct.
Complexity
| Metric | Value | Why |
|---|---|---|
| Time | O(n) | Each element is processed once |
| Space | O(n) | The dictionary may store many distinct values |
Hash Map Implementation
class Solution:
def containsNearbyDuplicate(self, nums: list[int], k: int) -> bool:
last_seen = {}
for i, num in enumerate(nums):
if num in last_seen and i - last_seen[num] <= k:
return True
last_seen[num] = i
return FalseCode Explanation
Create a dictionary:
last_seen = {}Scan with index and value:
for i, num in enumerate(nums):If the number appeared before and the distance is small enough:
if num in last_seen and i - last_seen[num] <= k:
return TrueUpdate the latest index:
last_seen[num] = iIf no valid pair was found:
return FalseSliding Window Set Version
Another way is to keep only the previous k values in a set.
At index i, the set represents values from indices:
i - k to i - 1If nums[i] already exists in the set, then it has appeared within distance k.
class Solution:
def containsNearbyDuplicate(self, nums: list[int], k: int) -> bool:
window = set()
for i, num in enumerate(nums):
if num in window:
return True
window.add(num)
if len(window) > k:
window.remove(nums[i - k])
return FalseThis also runs in O(n) time.
Sliding Window Explanation
For each index, we check only nearby previous values.
The set never contains more than k elements.
When the window becomes too large, remove the value that is now more than k positions behind.
The hash map version is slightly simpler to reason about. The window version uses O(k) space.
Complexity Comparison
| Method | Time | Space |
|---|---|---|
| Hash map latest index | O(n) | O(n) |
| Sliding window set | O(n) | O(k) |
| Brute force | O(n^2) | O(1) |
Testing
def run_tests():
s = Solution()
assert s.containsNearbyDuplicate([1, 2, 3, 1], 3) is True
assert s.containsNearbyDuplicate([1, 0, 1, 1], 1) is True
assert s.containsNearbyDuplicate([1, 2, 3, 1, 2, 3], 2) is False
assert s.containsNearbyDuplicate([1, 2, 3, 1], 2) is False
assert s.containsNearbyDuplicate([1, 1], 0) is False
assert s.containsNearbyDuplicate([99, 99], 1) is True
print("all tests passed")
run_tests()| Test | Why |
|---|---|
[1,2,3,1], k = 3 | Duplicate exactly at distance k |
[1,0,1,1], k = 1 | Nearby duplicate at adjacent indices |
[1,2,3,1,2,3], k = 2 | Duplicates exist but too far |
[1,2,3,1], k = 2 | Same value outside window |
[1,1], k = 0 | Distinct indices cannot have distance 0 |
[99,99], k = 1 | Small positive case |