昨天在leetcode上做了这道题,利用了C++中map存储结构的特征。
题意
Given an array of integers, find out whether there are two distinct indices i and j in the array such that the difference between nums[i] and nums[j] is at most t and the difference between i and j is at most k.
给定一个数组nums,判断:是不是存在两个不同的数(下标分别为i、j),它们满足如下两个条件
- | i - j | <= k
- | nums[i] - nums[j] | <= t
思路一:两层遍历(暴力法)
这个想法比较简单,我觉得leetcode的这个题也不可能会采用这样的方法。果然,提交下面的代码就超时了。
class Solution {
public:
bool containsNearbyAlmostDuplicate(vector<int>& nums, int k, int t) {
bool res = false;
int ns = nums.size();
for(int i = 0; i < ns - 1; i++)
{
//int max = 0;
for(int j = i + 1; j < ns && j <= i + k; j++)
{
long result = nums[i] - nums[j];
if(result > INT_MAX || result <= INT_MIN) return false;
if(abs(nums[i] - nums[j]) <= t)
{
return true;
}
}
}
return res;
}
};
思路二:利用Binary Search Tree,削减无意义搜索
这个思路利用了C++中的map数据结构的特性,搜索nums数据中在(nums[i] - t, nums[i] + t)
范围内的数,并判断其下标是否满足条件1。下面结合代码看一下。
class Solution {
private:
bool overflow(int one, int two)// 判断两个减数是否越界
{
long res = one - two;
if(res > INT_MAX || res <= INT_MIN) return true;
else return false;
}
public:
bool containsNearbyAlmostDuplicate(vector<int>& nums, int k, int t) {
if(t < 0) return false;
if(k < 1) return false;
bool res = false;
int ns = nums.size();
map<int, int> hmap; // map<nums[i], i>
for(int i = 0; i < ns; i++)
{
//int max = 0;
map<int, int>::iterator it = hmap.find(nums[i]);
if(it != hmap.end()) // 如果i之前有与nums[i]相同的值
{
if(abs(it->second - i) <= k) return true; // 下标之差不用判断是否越界。
it->second = i; // 如果不满足条件1,则将这个结点中的下标覆盖。这个比较好理解,it存在,说明它与之前值都未能满足条件。而i之后的nums肯定离i更近。
}
else
{
hmap[nums[i]] = i; // 若不存在,则插入
}
// 下面利用map迭代器的特征,对nums[i]两边(大小关系)相邻的值进行比较。这些值在(nums[i] - t, nums[i] + t)内。
map<int, int>::iterator temp_it = hmap.find(nums[i]);
if(temp_it != hmap.begin()) // it在begin位置,就无需向前了。
{
temp_it--;
while(temp_it != hmap.begin())
{
if(overflow(temp_it->first, nums[i])) return false;
if(abs(temp_it->first - nums[i]) > t) break; // 超出条件2的范围,跳出循环
if(abs(temp_it->second - i) <= k) return true;
temp_it--;
}
这个是为了处理begin元素
if(temp_it == hmap.begin())
{
if(overflow(temp_it->first, nums[i])) return false;
if(abs(temp_it->first - nums[i]) <= t)
{
if(abs(temp_it->second - i) <= k) return true;
}
}
}
// 向nums[i] + t方向查找。
temp_it = hmap.find(nums[i]);
temp_it++;
while(temp_it != hmap.end())
{
if(overflow(temp_it->first, nums[i])) return false;
if(abs(temp_it->first - nums[i]) > t) break;
if(abs(temp_it->second - i) <= k) return true;
temp_it++;
}
}
return res;
}
};
总结
通过这个题可以总结map的以下几点特性:
- map的存储结构,可以看成是一个搜索二叉树。
- map的迭代器的自加自减是在对二叉树进行中序遍历的过程,其实现方式应该是双向中序链表。
- map的键值的查找的平均时间复杂度是
log
级别。