Contains Duplicate系列三连- set,map的应用

又做完了一个系列,感觉收获良多。一共三道题,第一道和第二道都是easy,第三道是medium,不过我感觉第三道更接近hard。

217Contains Duplicate

Given an array of integers, find if the array contains any duplicates. Your function should return true if any value appears at least twice in the array, and it should return false if every element is distinct.

这道题有很多中方法可以做,我选择直接用set做。由于设计大量的简单的查找操作,所以使用基于hash实现的set更快,即unordered_set。

219Contains Duplicate II

Given an array of integers and an integer k, find out whether there are two distinct indices i and j in the array such that nums[i] = nums[j] and the absolute difference between i and j is at most k.

这道题在找到duplicate的同时还多了一个判断i,j差值的步骤。用map记录每个数字最近一次出现的下标,遇到重复的并其下标差小于k的返回true。同样使用基于hash实现的unordered_map。
    bool containsNearbyDuplicate(vector<int>& nums, int k) {
        unordered_map<int,int> intmap;
        unordered_map<int,int>::iterator iter;
        int i=0,size=nums.size();
        while(i<size)
        {
            if(intmap.count(nums[i]))
            {
                iter=intmap.find(nums[i]);
                if(i-iter->second<=k) 
                    return true;
                iter->second=i;
            }
            else
                intmap.insert(pair<int,int>(nums[i],i));
            i++;
        }
        return false;        
    }

220Contains Duplicate III

Given an array of integers, find out whether there are two distinct indices i and j in the array such that the absolute difference between nums[i] and nums[j] is at most t and the absolute difference between i and j is at most k.

前面两道题可以直接用set/map做出来,只要直接查找就可以了。这道题比第二道题还多了一个判断条件,使用什么样的数据结构是个需要仔细思考的问题。一开始想到用滑动窗口的思想,怎么快速的找到窗口内数组的最大值、最小值是个需要解决的问题。每次对窗口加入一个和删除一个数字,动态维护max、min,具体实现上我准备使用一个容量为k的最大堆和最小堆,每删除一个数就把这个数从堆中删去,每加入一个数就取堆顶判断是否符合差值小于t。由于STL中的priotity_queue没有delete的API,我自己又懒得写,所以最后没有实现这个方法。参考别人的做法,觉得还是很开眼的,先看代码

bool containsNearbyAlmostDuplicate(vector<int>& nums, int k, int t) {
    set<int> window; // set is ordered automatically
    for (int i = 0; i < nums.size(); i++) {
        if (i > k)
            window.erase(nums[i-k-1]);
        int lowerBound = nums[i] < 0 && nums[i] - t > 0 ? INT_MIN : nums[i] - t;
        auto pos = window.lower_bound(lowerBound);
        if (pos != window.end() && *pos - nums[i] <= t)
            return true;
        window.insert(nums[i]);
    }
    return false;
}

使用set而不是unordered_set是很精髓的地方。这里用到了set的lower_bound(val)这个API,之前我并不知道这是干嘛的。由于set是基于RB Tree实现的,也就是一种平衡BST,所以具有支持顺序遍历的特性,因此STL中支持API:lower_bound(val)用来返回第一个大于等于val的元素的迭代器。官方文档的说明是:

Returns an iterator pointing to the first element in the container which is not considered to go before val (i.e., either it is equivalent or goes after).

这里的go before涉及到set的顺序遍历的特性。set根据less方法定义红黑树内元素的位置关系,在没有任何设置的默认情况下,小于val的数字插入到左孩子,和最常用的BST定义相同。因此如果对一个由整数构成的set用for(auto element : set)或者iterator it = begin()的方式对它进行顺序遍历、输出的话,会按照从小到大的顺序输出。

上面的代码中找到大于等于nums[i] - t的第一个元素,例如nums[i] = 100, t = 10,pos就代表第一个大于等于90的元素。如果 pos - nums[i] <= t 就说明存在题目中定义的duplicate(如果pos < nums[i],前面又限制了pos >= nums[i] - t的,所以只要pos - nums[i] < 0就符合duplicate的定义,pos > nums[i]的情况就更明显了)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值