Given an array of integers and an integer k, return true if and only if there are two distinct indices i and j in the array such that nums[i] = nums[j] and the difference between i and j is at most k.
这道题是之前那道Contains Duplicate 包含重复值的延伸,不同之处在于那道题只要我们判断下数组中是否有重复值,而这道题限制了数组中只许有一组重复的数字,而且他们坐标差不能超过k。那么我们首先需要一个哈希表,来记录每个数字和其坐标的映射,然后我们需要一个变量d来记录第一次出现重复数字的坐标差。由于题目要求只能有一组重复的数字,所以我们在遇到重复数字时,首先判断d是否已经存了值,如果d已经有值了,说明之前有过了重复数字,则直接返回false即可。如果没有,则此时给d附上值。在网上看到有些解法在这里就直接判断d和k的关系然后返回结果了,其实这样是不对的。因为题目要求只能有一组重复数,就是说如果后面又出现了重复数,就没法继续判断了。所以正确的做法应该是扫描完整个数组后在判断,先看d有没有存入结果,如果没有,则说明没出现过重复数, 返回false即可。如果d有值,再跟k比较,返回对应的结果。OJ的test case没有包含所有的情况,比如当nums = [1, 2, 3, 1, 3], k = 3时,实际上应该返回false,但是有些返回true的算法也能通过OJ。个人认为正确的解法应该入下:
class Solution {
public:
bool containsNearbyDuplicate(vector<int>& nums, int k) {
map<int, int> m;
for (int i = 0; i < nums.size(); ++i) {
if (m.find(nums[i]) != m.end() && i - m[nums[i]] <= k) return true;
else m[nums[i]] = i;
}
return false;
}
};
其他:
思路分析:这题比较简单,可以直接定义一个长度最大为k的滑动窗口,用一个set维护窗口内的数字判断是否出现重复,使用两个指针start和end标记滑动窗口的两端,初始都是0,然后end不断进行扩展,扫描元素判断是否出现重复元素,直到发现end-start>k, 就开始移动start,并且在set中移除对应的元素。如果以为扫描到数组末尾还没有发现重复元素,那就可以返回false。时间复杂度和空间复杂度都是O(N)。
public class Solution {
public boolean containsNearbyDuplicate(int[] nums, int k) {
//0842
Set<Integer> appearedNum = new HashSet<Integer>();
int start = 0, end = 0;
for(int i = 0; i < nums.length; i++){
if(!appearedNum.contains(nums[i])){
appearedNum.add(nums[i]);
end++;
} else return true;
if(end - start > k) {
appearedNum.remove(nums[start]);
start++;
}
}
return false;
//0848
}
}
class Solution {
public:
bool containsNearbyDuplicate(vector<int>& nums, int k) {
map<int, int> buf;
int n = nums.size();
for (int i = 0; i < n; i++)
{
if (buf.find(nums[i]) != buf.end() && i-buf[nums[i]] <= k)
{
return true;
}
else
{
buf[nums[i]] = i;
}
}
return false;
}
};