持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第18天,点击查看活动详情
每日刷题 2022.06.16
- leetcode原题链接:leetcode.cn/problems/k-…
- 难度:中等
- 方法:set、二分、map
题目
- 给你一个整数数组
nums和一个整数k,请你在数组中找出 不同的k-diff数对,并返回不同的k-diff数对的数目。 k-diff数对定义为一个整数对(nums[i], nums[j]),并满足下述全部条件:0 <= i, j < nums.lengthi != jnums[i] - nums[j] == k
- 注意,
|val|表示val的绝对值。
示例
- 示例1
输入:nums = [3, 1, 4, 1, 5], k = 2
输出:2
解释:数组中有两个 2-diff 数对, (1, 3) 和 (3, 5)。
尽管数组中有两个 1 ,但我们只应返回不同的数对的数量。
- 示例2
输入: nums = [1, 3, 1, 5, 4], k = 0
输出: 1
解释: 数组中只有一个 0-diff 数对,(1, 1) 。
提示
1 <= nums.length <= 10^4-10^7 <= nums[i] <= 10^70 <= k <= 10^7
解题思路
去重+map集合
- 首先数组去重,可以避免出现相同的数对(例:
(1,3)和(3,1),虽然下标是不同的,但是会被认定为重复的数对),对结果进行重复的计算。 - 去重后的数组记为
arr,对arr数组中的每一个元素,从其当前所在的位置往后找,直到找到数组的最后一个元素结束,在每次查找的过程中,记录当前位置i,可以加k得到的j,找到符合的数对就将结果++。- 这样的话,还有一种情况会漏掉。当
k = 0的时候,也就是(1,1),即:nums[i]和nums[j]相等的情况下,就会因为数组去重而没有计算上。 因此,需要在k = 0的时候,使用map集合统计没有去重前的数组中每个元素出现的次数,当某个元素出现的次数大于1的时候,就表示其可以形成一个k = 0的数对,因此就需要给结果++。
- 这样的话,还有一种情况会漏掉。当
二分查找(两数之和的做法类似)+ 排序
- 两数之和的总结,可以查看这篇文章两数之和
- 相应的转换:两数之和对于数组中的每一位,都可以使用
target减去当前的元素值,然后在数组中再去查找符合的值 - 而本题就是,需要对于数组中的每一个元素去查找,
当前元素+k之后的数值。通过观察可以知道查找可以使用二分,因为其具有二段性。
map集合
- 重点:去重的手法
- 同样是遍历数组中的每一个元素,这次使用
map集合来记录当前位置之前的元素,因为不能存在相同的数对,因此我们可以保证数对的前面元素是最小值,这样就不会出现(1,3)和(3,1)这种重复的情况,因为每次都是将最小的放在前面才进行记录。 - 又因为题目中所给的
k值是唯一的,那么对于每个数对的前一个元素来说,一定会有一个唯一的后一个元素与之匹配,因为是唯一的,所以可以使用数对中的前一个元素来代表整个数对。 - 使用
map匹配的时候,应该匹配i + k 和 i - k两个都应该遍历到。 - 最终输出存储的所有的数对前一个元素的数组的长度即可(答案)。
AC代码
/**
* @param {number[]} nums
* @param {number} k
* @return {number}
*/
var findPairs = function(nums, k) {
// 首先先去重
// 然后计算数对
let map = new Map(), ans = 0;
// 需要特判k=0
if(k == 0) {
nums.forEach(value => {
map.set(value, (map.get(value) || 0) + 1);
});
map.forEach(value => {
if(value >= 2) ans++;
});
}
// 对数组进行去重,并存储在set中
const set = new Set(nums);
const arr = [...set], n = arr.length;
for(let i = 0; i < n; i++) {
for(let j = i + 1; j < n; j++) {
// 查找两数之差是否等于k,可以优化,使用二分
// 常规的做题方法,挨个测试,计算两数之差是否等于k
if(Math.abs(arr[i] - arr[j]) == k){
ans++;
}
}
}
return ans;
};