532. K-diff Pairs in an Array
Problem Description
You are given an array of integers nums
and an integer k
. Your task is to find the number of unique k-diff pairs in the array.
A k-diff pair is defined as a pair of integers (nums[i], nums[j])
from the array that satisfies all of the following conditions:
- Both indices
i
andj
are valid positions in the array:0 <= i, j < nums.length
- The indices must be different:
i != j
- The absolute difference between the two values equals
k
:|nums[i] - nums[j]| == k
The key requirement is that pairs must be unique - meaning if you have multiple occurrences of the same pair of values, they should be counted only once.
For example:
- If
nums = [3, 1, 4, 1, 5]
andk = 2
, the k-diff pairs are(1, 3)
and(3, 5)
, so the answer is2
- If
nums = [1, 2, 3, 4, 5]
andk = 1
, there are four k-diff pairs:(1, 2)
,(2, 3)
,(3, 4)
,(4, 5)
, so the answer is4
- If
nums = [1, 3, 1, 5, 4]
andk = 0
, the only k-diff pair is(1, 1)
(since1
appears twice), so the answer is1
The solution uses a hash table approach where we track the smaller value of each valid pair to ensure uniqueness. As we traverse the array, we check if the current number can form a valid k-diff pair with previously seen numbers by checking if x - k
or x + k
exists in our visited set.
Intuition
The key insight is that for any number x
in the array, it can form a k-diff pair in only two possible ways:
- With a smaller number:
(x - k, x)
where the difference isx - (x - k) = k
- With a larger number:
(x, x + k)
where the difference is(x + k) - x = k
Since we want to count unique pairs, we need a way to avoid counting the same pair multiple times. A clever approach is to always store the smaller value of each pair in a set. This ensures that even if we encounter the same pair from different positions in the array, we only count it once.
As we traverse the array, for each number x
:
- If we've already seen
x - k
, then(x - k, x)
forms a valid pair, and we storex - k
(the smaller value) - If we've already seen
x + k
, then(x, x + k)
forms a valid pair, and we storex
(the smaller value)
By using this approach with two sets - one (vis
) to track all numbers we've seen so far, and another (ans
) to store the smaller values of valid pairs - we can efficiently find all unique k-diff pairs in a single pass through the array.
The beauty of this solution is that it naturally handles duplicates and ensures uniqueness. For example, if we have [1, 3, 1, 5, 4]
with k = 2
, when we encounter the second 1
, we won't add a duplicate pair because 1 - 2 = -1
would be the same smaller value we might have already stored.
Learn more about Two Pointers, Binary Search and Sorting patterns.
Solution Approach
The solution uses a hash table strategy with two sets to efficiently find all unique k-diff pairs in a single pass through the array.
Data Structures Used:
ans
: A set that stores the smaller value of each valid k-diff pair to ensure uniquenessvis
: A set that keeps track of all numbers we've encountered so far
Algorithm Steps:
-
Initialize two empty sets:
ans
for storing unique pairs (represented by their smaller values) andvis
for tracking visited numbers. -
Iterate through each number
x
in the array:a. Check for smaller partner: If
x - k
exists invis
, it means we can form a pair(x - k, x)
with differencek
. Addx - k
toans
(the smaller value of the pair).b. Check for larger partner: If
x + k
exists invis
, it means we can form a pair(x, x + k)
with differencek
. Addx
toans
(the smaller value of the pair).c. Mark current number as visited: Add
x
tovis
for future iterations. -
Return the size of
ans
, which represents the count of unique k-diff pairs.
Why this works:
- By storing only the smaller value of each pair in
ans
, we automatically handle uniqueness - even if the same pair appears multiple times through different combinations, it will only be stored once - The order of traversal doesn't matter because we check both directions (
x - k
andx + k
) for each number - The time complexity is O(n) since we traverse the array once, and set operations are O(1) on average
- The space complexity is O(n) for storing the visited numbers and unique pairs
Example walkthrough with nums = [3, 1, 4, 1, 5]
and k = 2
:
- Process
3
:vis = {3}
,ans = {}
- Process
1
: Check1-2=-1
(not in vis), check1+2=3
(in vis), add1
to ans.vis = {3, 1}
,ans = {1}
- Process
4
: Check4-2=2
(not in vis), check4+2=6
(not in vis).vis = {3, 1, 4}
,ans = {1}
- Process
1
: Check1-2=-1
(not in vis), check1+2=3
(in vis), add1
to ans (already there).vis = {3, 1, 4}
,ans = {1}
- Process
5
: Check5-2=3
(in vis), add3
to ans. Check5+2=7
(not in vis).vis = {3, 1, 4, 5}
,ans = {1, 3}
- Final answer:
len(ans) = 2
Ready to land your dream job?
Unlock your dream job with a 3-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through a detailed example with nums = [1, 2, 4, 4, 3, 3, 0, 9, 2, 3]
and k = 3
.
We'll track two sets:
vis
: numbers we've seen so farans
: smaller values of valid k-diff pairs (for uniqueness)
Step-by-step process:
-
Process 1:
- Check if
1 - 3 = -2
is invis
? No - Check if
1 + 3 = 4
is invis
? No - Add 1 to
vis
- State:
vis = {1}
,ans = {}
- Check if
-
Process 2:
- Check if
2 - 3 = -1
is invis
? No - Check if
2 + 3 = 5
is invis
? No - Add 2 to
vis
- State:
vis = {1, 2}
,ans = {}
- Check if
-
Process 4:
- Check if
4 - 3 = 1
is invis
? Yes! Form pair (1, 4), add 1 toans
- Check if
4 + 3 = 7
is invis
? No - Add 4 to
vis
- State:
vis = {1, 2, 4}
,ans = {1}
- Check if
-
Process 4 (duplicate):
- Check if
4 - 3 = 1
is invis
? Yes! Add 1 toans
(already exists) - Check if
4 + 3 = 7
is invis
? No - Add 4 to
vis
(already exists) - State:
vis = {1, 2, 4}
,ans = {1}
- Check if
-
Process 3:
- Check if
3 - 3 = 0
is invis
? No - Check if
3 + 3 = 6
is invis
? No - Add 3 to
vis
- State:
vis = {1, 2, 3, 4}
,ans = {1}
- Check if
-
Process 3 (duplicate):
- Check if
3 - 3 = 0
is invis
? No - Check if
3 + 3 = 6
is invis
? No - Add 3 to
vis
(already exists) - State:
vis = {1, 2, 3, 4}
,ans = {1}
- Check if
-
Process 0:
- Check if
0 - 3 = -3
is invis
? No - Check if
0 + 3 = 3
is invis
? Yes! Form pair (0, 3), add 0 toans
- Add 0 to
vis
- State:
vis = {0, 1, 2, 3, 4}
,ans = {0, 1}
- Check if
-
Process 9:
- Check if
9 - 3 = 6
is invis
? No - Check if
9 + 3 = 12
is invis
? No - Add 9 to
vis
- State:
vis = {0, 1, 2, 3, 4, 9}
,ans = {0, 1}
- Check if
-
Process 2 (duplicate):
- Check if
2 - 3 = -1
is invis
? No - Check if
2 + 3 = 5
is invis
? No - Add 2 to
vis
(already exists) - State:
vis = {0, 1, 2, 3, 4, 9}
,ans = {0, 1}
- Check if
-
Process 3 (another duplicate):
- Check if
3 - 3 = 0
is invis
? Yes! Form pair (0, 3), add 0 toans
(already exists) - Check if
3 + 3 = 6
is invis
? No - Add 3 to
vis
(already exists) - State:
vis = {0, 1, 2, 3, 4, 9}
,ans = {0, 1}
- Check if
Final Result: The set ans
contains {0, 1}, representing 2 unique k-diff pairs:
- Pair (0, 3) with difference 3
- Pair (1, 4) with difference 3
The answer is 2.
Solution Implementation
1class Solution:
2 def findPairs(self, nums: List[int], k: int) -> int:
3 # Set to store unique pairs (represented by the smaller value in each pair)
4 unique_pairs = set()
5
6 # Set to track numbers we've already seen
7 seen_numbers = set()
8
9 # Iterate through each number in the array
10 for current_num in nums:
11 # Check if (current_num - k, current_num) forms a valid pair
12 # If current_num - k was seen before, we found a pair
13 if current_num - k in seen_numbers:
14 unique_pairs.add(current_num - k)
15
16 # Check if (current_num, current_num + k) forms a valid pair
17 # If current_num + k was seen before, we found a pair
18 if current_num + k in seen_numbers:
19 unique_pairs.add(current_num)
20
21 # Add current number to the set of seen numbers
22 seen_numbers.add(current_num)
23
24 # Return the count of unique k-diff pairs
25 return len(unique_pairs)
26
1class Solution {
2 public int findPairs(int[] nums, int k) {
3 // Set to store unique smaller values of valid pairs (num1, num2) where num1 < num2
4 Set<Integer> uniquePairs = new HashSet<>();
5 // Set to track numbers we've already seen during iteration
6 Set<Integer> visitedNumbers = new HashSet<>();
7
8 // Iterate through each number in the array
9 for (int currentNum : nums) {
10 // Check if (currentNum - k, currentNum) forms a valid pair
11 // If currentNum - k exists in visited set, then we found a pair
12 if (visitedNumbers.contains(currentNum - k)) {
13 // Store the smaller value of the pair
14 uniquePairs.add(currentNum - k);
15 }
16
17 // Check if (currentNum, currentNum + k) forms a valid pair
18 // If currentNum + k exists in visited set, then we found a pair
19 if (visitedNumbers.contains(currentNum + k)) {
20 // Store the smaller value of the pair
21 uniquePairs.add(currentNum);
22 }
23
24 // Mark current number as visited
25 visitedNumbers.add(currentNum);
26 }
27
28 // Return the count of unique pairs
29 return uniquePairs.size();
30 }
31}
32
1class Solution {
2public:
3 int findPairs(vector<int>& nums, int k) {
4 // Set to store unique pairs (represented by the smaller value in each pair)
5 unordered_set<int> uniquePairs;
6
7 // Set to track numbers we've already processed
8 unordered_set<int> visitedNumbers;
9
10 // Iterate through each number in the array
11 for (int currentNum : nums) {
12 // Check if (currentNum - k, currentNum) forms a valid pair
13 // If currentNum - k exists in visited set, we found a pair
14 if (visitedNumbers.count(currentNum - k)) {
15 uniquePairs.insert(currentNum - k); // Store smaller value of the pair
16 }
17
18 // Check if (currentNum, currentNum + k) forms a valid pair
19 // If currentNum + k exists in visited set, we found a pair
20 if (visitedNumbers.count(currentNum + k)) {
21 uniquePairs.insert(currentNum); // Store smaller value of the pair
22 }
23
24 // Mark current number as visited
25 visitedNumbers.insert(currentNum);
26 }
27
28 // Return the count of unique k-diff pairs
29 return uniquePairs.size();
30 }
31};
32
1/**
2 * Finds the number of unique k-diff pairs in the array.
3 * A k-diff pair is defined as an integer pair (nums[i], nums[j]) where:
4 * - i != j
5 * - nums[i] - nums[j] = k
6 *
7 * @param nums - The input array of integers
8 * @param k - The target difference value
9 * @returns The number of unique k-diff pairs
10 */
11function findPairs(nums: number[], k: number): number {
12 // Set to store unique smaller values of valid pairs to avoid duplicates
13 const uniquePairStarts = new Set<number>();
14
15 // Set to track all numbers we've seen so far
16 const visitedNumbers = new Set<number>();
17
18 // Iterate through each number in the array
19 for (const currentNum of nums) {
20 // Check if (currentNum - k, currentNum) forms a valid pair
21 // If currentNum - k was seen before, then we have a pair
22 if (visitedNumbers.has(currentNum - k)) {
23 uniquePairStarts.add(currentNum - k);
24 }
25
26 // Check if (currentNum, currentNum + k) forms a valid pair
27 // If currentNum + k was seen before, then we have a pair
28 if (visitedNumbers.has(currentNum + k)) {
29 uniquePairStarts.add(currentNum);
30 }
31
32 // Mark current number as visited
33 visitedNumbers.add(currentNum);
34 }
35
36 // Return the count of unique pairs
37 return uniquePairStarts.size;
38}
39
Time and Space Complexity
The time complexity is O(n)
, where n
is the length of the array nums
. This is because the algorithm iterates through the array exactly once, and for each element, it performs constant-time operations: checking membership in sets (O(1)
average case), adding elements to sets (O(1)
average case), and performing arithmetic operations (x - k
and x + k
).
The space complexity is O(n)
. In the worst case, both the ans
set and the vis
set can contain up to n
distinct elements. The vis
set will contain all unique elements from the input array, which can be at most n
elements. The ans
set stores the smaller values of valid pairs, which in the worst case (when k = 0
and all elements are unique) can also be up to n
elements. Therefore, the total space used is proportional to n
.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Handling the k=0 Special Case Incorrectly
The Pitfall:
When k = 0
, we're looking for pairs where both numbers are the same (i.e., duplicates). The current solution has a subtle bug: it adds numbers to seen_numbers
immediately, so when we encounter the same number again, it will find itself and incorrectly count pairs.
Example of the Bug:
With nums = [1, 1, 1, 1]
and k = 0
:
- First
1
: Nothing found, add to seen - Second
1
: Finds1-0=1
and1+0=1
in seen, adds1
to unique_pairs - Third
1
: Again finds1
in seen, but1
is already in unique_pairs - Fourth
1
: Same as above
This seems to work, but consider nums = [1, 2, 1]
and k = 0
:
- Process
1
: Nothing found - Process
2
: Nothing found - Process
1
: Finds1
in seen, adds to unique_pairs
The issue is more apparent with the order-dependent nature when we have multiple distinct duplicates.
The Correct Approach:
For k = 0
, we should count how many numbers appear at least twice:
class Solution:
def findPairs(self, nums: List[int], k: int) -> int:
if k == 0:
# Special case: count numbers that appear at least twice
from collections import Counter
freq = Counter(nums)
return sum(1 for count in freq.values() if count >= 2)
# For k > 0, use the original approach
unique_pairs = set()
seen_numbers = set()
for current_num in nums:
if current_num - k in seen_numbers:
unique_pairs.add(current_num - k)
if current_num + k in seen_numbers:
unique_pairs.add(current_num)
seen_numbers.add(current_num)
return len(unique_pairs)
2. Negative k Values
The Pitfall:
The problem states we need |nums[i] - nums[j]| == k
, which means the absolute difference. Since absolute values are always non-negative, a negative k
should return 0 immediately.
The Fix: Add validation at the beginning:
class Solution:
def findPairs(self, nums: List[int], k: int) -> int:
if k < 0:
return 0
# Rest of the solution...
3. Understanding Pair Uniqueness
The Pitfall:
Developers might misunderstand what "unique pairs" means. The pairs (1, 3)
and (3, 1)
are considered the same pair since we're dealing with absolute difference. The solution correctly handles this by always storing the smaller value of each pair, but it's important to understand why this works.
Key Insight:
By consistently storing the smaller value and checking both x - k
and x + k
, we ensure:
- Each valid pair is found exactly once (regardless of order)
- Duplicate pairs from different index combinations are automatically deduplicated
Which of the following shows the order of node visit in a Breadth-first Search?
Recommended Readings
Tech Interview Pattern Two Pointers Introduction If you prefer videos here's a super quick introduction to Two Pointers div class responsive iframe iframe src https www youtube com embed xZ4AfXHQ1VQ title YouTube video player frameborder 0 allow accelerometer autoplay clipboard write encrypted media gyroscope picture in picture allowfullscreen iframe div Two pointers is a common interview
Binary Search Speedrun For each of the Speedrun questions you will be given a binary search related problem and a corresponding multiple choice question The multiple choice questions are related to the techniques and template s introduced in the binary search section It's recommended that you have gone through at
Sorting Summary Comparisons We presented quite a few sorting algorithms and it is essential to know the advantages and disadvantages of each one The basic algorithms are easy to visualize and easy to learn for beginner programmers because of their simplicity As such they will suffice if you don't know any advanced
Want a Structured Path to Master System Design Too? Don’t Miss This!