首先第一题leetcode 217 contains duplicate是要在一组正整数里面找出重复的那一个,可以构造一个哈希表来查找,也可以构造二叉查找树来查找,哈希表查找的时间效率比二叉查找树好,但是二叉查找树的空间效率比哈希表好,都可以试试。基于哈希表的可以用unordered_set,基于红黑树(一种平衡二叉查找树)的可以用set。
class Solution {
public:
bool containsDuplicate(vector<int>& nums) {
unordered_set<int> hashTable;
for (int i = 0; i < nums.size(); i++) {
if (hashTable.find(nums[i]) != hashTable.end()) {
return true;
}
hashTable.insert(nums[i]);
}
return false;
}
};
看到只有一行的做法,记录一下:
class Solution {
public:
bool containsDuplicate(vector<int>& nums) {
return nums.size() > set<int>(nums.begin(), nums.end()).size();
}
};
第二题leetcode 219 contains duplicate在第一题的基础上,还给出了一个k,还要看重复的那个数字的下标的距离是不是最多是k,那我就在哈希表里面存储每个数字的下标,这样的话可以用unordered_map实现。注意这道题重复的数字可能多次出现,只要有一对满足这个条件就可以了。
class Solution {
public:
bool containsNearbyDuplicate(vector<int>& nums, int k) {
unordered_map<int, int> hashTable;
for (int i = 0; i < nums.size(); i++) {
if (hashTable.find(nums[i]) != hashTable.end()) {
if ((i - hashTable[nums[i]]) <= k) {
return true;
} else {
hashTable[nums[i]] = i;
}
}
hashTable[nums[i]] = i;
}
return false;
}
};
有个方法是可以维持哈希表或集合里面只有k和元素,记录一下:
bool containsNearbyDuplicate(vector<int>& nums, int k) {
set<int> cand;
for (int i = 0; i < nums.size(); i++) {
if (i > k) cand.erase(nums[i-k-1]);
if (!cand.insert(nums[i]).second) return true;
}
return false;
}
第三题leetcode 220,不单只给下标给一个范围,而且将原来找重复数字的条件放松到查找一个范围以内的数字都可以。一开始的思路没有用任何附加的数据结构,只是单纯的给每个数字的前k个数字都找一次有没有符合条件的数字,这样的复杂度是O(NK),结果超时了。
class Solution {
public:
bool containsNearbyAlmostDuplicate(vector<int>& nums, int k, int t) {
for (int i = 0; i < nums.size(); i++) {
int iterCount = k;
while(iterCount) {
if (i - iterCount >= 0 && labs(long(nums[i - iterCount]) - long(nums[i])) <= t) {
return true;
}
iterCount--;
}
}
return false;
}
};
那就换一个思路,用一个哈希表存放数字的下标,先得到数字的范围,再在哈希表里找有没有符合条件的数字,再判断它们的下标是不是满足要求。这样的复杂度是O(NT)。当t很大的时候也会超时。
class Solution {
public:
bool containsNearbyAlmostDuplicate(vector<int>& nums, int k, int t) {
unordered_map<long, int> hashTable;
for (int i = 0; i < nums.size(); i++) {
int numRange = 0;
while (numRange <= t) {
// cout << numRange << endl;
if (hashTable.find(long(nums[i]) - numRange) != hashTable.end() && i - hashTable[long(nums[i]) - numRange] <= k) {
return true;
}
if (hashTable.find(long(nums[i]) + numRange) != hashTable.end() && i - hashTable[long(nums[i]) + numRange] <= k) {
return true;
}
numRange++;
}
hashTable[nums[i]] = i;
}
return false;
}
};
其实这里的重复比较都是因为没有记录下来k个候选数字的大小关系,二叉搜索树比哈希好就好在了它是有序的,可以在log(N)的时间(set是用红黑树实现的)里面进行插入删除操作,并且维持这一堆元素的大小关系,每次遇到新元素就更新一下这棵树,使得里面始终只有k个候选数字,然后在遇到新数字nums[i]的时候就可以找树里面有没有符合条件范围的数字,这时候也不用一个一个比较,因为树里面的数字都是有序的,每一次只拿第一个大于等于nums[i]-t的数字,如果树里面没有大于等于nums[i]-t的数字,说明这棵树都不满足条件,需要更新,如果有的话,还要看这个数字是不是小于等于nums[i]+t的,如果是的话,就可以直接返回true了,如果不是的话,就说明这棵树里面除了小于nums[i]-t的数字之外其他的数字都是大于nums[i]+t的,因为这棵树是有序的,他的后一个数肯定大于他本身(没有重复数字的情况下),所以复杂度只是O(Nlog(K))。不过还是要注意溢出的问题。
class Solution {
public:
bool containsNearbyAlmostDuplicate(vector<int>& nums, int k, int t) {
set<long> balancedBST;
for (int i = 0; i < nums.size(); i++) {
set<long>::iterator it = balancedBST.lower_bound(long(nums[i]) - t);
if (it != balancedBST.end() && long(*it) - nums[i] <= t) {
// cout << i << " " << nums[i] << " " << *it << endl;
return true;
}
balancedBST.insert(nums[i]);
if (i >= k) {
balancedBST.erase(nums[i - k]);
}
}
return false;
}
};