K-diff Pairs in an Array LT532
Given an array of integers and an integer k, you need to find the number of unique k-diff pairs in the array. Here a k-diff pair is defined as an integer pair (i, j), where i and j are both numbers in the array and their absolute difference is k.
Example 1:
Input: [3, 1, 4, 1, 5], k = 2 Output: 2 Explanation: There are two 2-diff pairs in the array, (1, 3) and (3, 5).
Although we have two 1s in the input, we should only return the number of unique pairs.
Example 2:
Input:[1, 2, 3, 4, 5], k = 1 Output: 4 Explanation: There are four 1-diff pairs in the array, (1, 2), (2, 3), (3, 4) and (4, 5).
Example 3:
Input: [1, 3, 1, 5, 4], k = 0 Output: 1 Explanation: There is one 0-diff pair in the array, (1, 1).
Note:
- The pairs (i, j) and (j, i) count as the same pair.
- The length of the array won't exceed 10,000.
- All the integers in the given input belong to the range: [-1e7, 1e7].
Idea 1. At first think it's the same as two sum with a HashSet should be enough, then realise the special case for k = 0, we need to count duplicates only once, hence the frequency is needed, a HashMap with (num, frequency) pair is suitable.
++count if k == 0 and this is the second duplicates, there is 1 before, prev.get(num) == 1
++count if k != 0, this number is the first time appear and prev.containsKey(num - k) or prev.containsKey(num + k), Note there might both values exisits, num - k and num + k, for example if num = 3, k = 2, num-k = 1, num + k = 5, we need to count twice.
Time complexity: O(N), 1 scan
Space complexity: O(N), worest case O(N)
1 class Solution { 2 public int findPairs(int[] nums, int k) { 3 if(k < 0) { 4 return 0; 5 } 6 Map<Integer, Integer> prev = new HashMap<>(); 7 8 int count = 0; 9 for(int num: nums) { 10 int old = prev.getOrDefault(num, 0); 11 if(k == 0 && old == 1) { 12 ++count; 13 } 14 else if(k != 0 && old == 0) { 15 if(prev.containsKey(num-k)) { 16 ++count; 17 } 18 if(prev.containsKey(num+k)) { 19 ++count; 20 } 21 } 22 prev.put(num, old+1); 23 24 } 25 26 return count; 27 } 28 }
Idea 1.b Scan the map to store the pair(num, frequencey) first,
++count if k == 0 && prev.get(num) >= 2
++count if k > 0 && prev.containsKey(num-k), one direction to compare is ok to avoid double counts (num-k, num+k), 3-> 5 or 5-> 3
Time complexity: O(N), 2 scan
Space complexity: O(M), worst case O(N)
1 class Solution { 2 public int findPairs(int[] nums, int k) { 3 if(k < 0) { 4 return 0; 5 } 6 Map<Integer, Integer> counts = new HashMap<>(); 7 8 int count = 0; 9 for(int num: nums) { 10 counts.put(num, counts.getOrDefault(num, 0) + 1); 11 } 12 13 for(int num: counts.keySet()) { 14 if(k == 0 && counts.get(num) >= 2) { 15 ++count; 16 } 17 else if(k != 0 && counts.containsKey(num - k)) { 18 ++count; 19 } 20 } 21 22 return count; 23 } 24 }
Use Map.Entry<Integer, Integer> entry, not able to use forEach, as lambda can only contains effective final variable.
1 class Solution { 2 public int findPairs(int[] nums, int k) { 3 Map<Integer, Integer> counts = new HashMap<>(); 4 5 for(int num: nums) { 6 counts.put(num, counts.getOrDefault(num, 0) + 1); 7 } 8 9 int count = 0; 10 for(Map.Entry<Integer, Integer> entry: counts.entrySet()) { 11 if(k == 0 && entry.getValue() >= 2) { 12 ++count; 13 } 14 else if(k > 0 && counts.containsKey(entry.getKey() - k)) { 15 ++count; 16 } 17 } 18 19 return count; 20 } 21 }
Idea 2. two pointer, sort the array, like sliding the window,
if the difference is bigger than k, move left pointer
if the difference is smaller than k, move right pointer
if the difference is equal to k, ++count, move both left pointer and right pointer to next position which has different values.
Note: right pointer is always before the left pointer, not even equal, as they need to point to different numbers.
Time complexity: O(NlogN)
Space complexity: O(1)
class Solution { public int findPairs(int[] nums, int k) { if(k < 0) { return 0; } Arrays.sort(nums); int count = 0; for(int left = 0, right = 1; right < nums.length;) { if(nums[right] - nums[left] > k) { ++left; } else if(nums[right] - nums[left] < k) { ++right; } else { ++count; int leftVal = nums[left]; int rightVal = nums[right]; while(right < nums.length && nums[right] == rightVal) { ++right; } while(left < nums.length && nums[left] == leftVal) { ++left; } } if(right == left) { // right should be always before left ++right; } } return count; } }
Slightly different way to move pointers, also two pointers, move right pointer as main loop, move right pointer to the last element has the same value, if difference is bigger than k, move left, otherwise ++count.
The following code is refactored during sleeping mode...
Time complexity: O(NlogN)
Space complexity: O(1)
1 class Solution { 2 public int findPairs(int[] nums, int k) { 3 if(k < 0) { 4 return 0; 5 } 6 7 Arrays.sort(nums); 8 int count = 0; 9 10 for(int left = 0, right = 0; right < nums.length; ++right) { 11 while(right + 1 < nums.length && nums[right] == nums[right+1]) { 12 ++right; 13 } 14 15 while(nums[right] - nums[left] > k) { 16 ++left; 17 } 18 19 if(k == 0 && nums[right] == nums[left] && right - left >= 1) { 20 ++count; 21 } 22 else if(k > 0 && nums[right] - nums[left] == k) { 23 ++count; 24 } 25 } 26 27 return count; 28 } 29 }