(查找表)存在重复元素I、II、III

版权声明:转载请注明出处!谢谢! https://blog.csdn.net/qq_28114615/article/details/86307198

1.存在重复元素 I

1.1 题目描述

1.2 题目分析

1.3 代码实现

2.存在重复元素 II

2.1 题目描述

2.2 题目分析

2.3 代码实现

3.存在重复元素 III

3.1 题目描述

3.2 题目分析

3.3 代码实现


1.存在重复元素 I

1.1 题目描述

        给定一个整数数组,判断是否存在重复元素。如果任何值在数组中出现至少两次,函数返回 true。如果数组中每个元素都不相同,则返回 false。

1.2 题目分析

         这道题目就非常简单了,有很多种做法,可以直接用映射来计数,来判断是否有元素的计数大于1.

1.3 代码实现

bool containsDuplicate(vector<int>& nums) {
        
        unordered_map<int,int>m;
        
        for(auto x:nums)
        {
            m[x]++;
            if(m[x]!=1)return true;
        }
        return false;
    }

2.存在重复元素 II

2.1 题目描述

        给定一个整数数组和一个整数 k,判断数组中是否存在两个不同的索引 i 和 j,使得 nums [i] = nums [j],并且 i 和 j 的差的绝对值最大为 k

2.2 题目分析

        II的题意很明确,就是判断数组中是否存在两个相同的数,这里就不多说了,可以直接用映射表,构建元素->索引的映射表来做。

2.3 代码实现

bool containsNearbyDuplicate(vector<int>& nums, int k) {
        
        unordered_map<int,int>m;
        
        for(int i=0;i<nums.size();i++)
        {
            if(m.find(nums[i])!=m.end())  //如果当前元素已存在
            {
                if(i-m[nums[i]]<=k)return true; //并且已存在的元素的索引符合要求
            }
            m[nums[i]]=i;
        }
        return false;
    }

3.存在重复元素 III

3.1 题目描述

       给定一个整数数组,判断数组中是否有两个不同的索引 i 和 j,使得 nums [i] 和 nums [j] 的差的绝对值最大为 t,并且 i 和 j 之间的差的绝对值最大为 ķ

3.2 题目分析

       与II相比,这道题的变化在于扩大了符合条件的nums[j]的范围,只需要保证nums[i]和nums[j]绝对值之差不超过t即可,这就相当于在nums[i]-t~nums[i]+t范围中去找到和nums[i]相等的数,其他的与II相似。

       那么,该如何来找符合范围里的数呢?这里就可以考虑set集合中的一个常用函数lower_bound(val),这个函数的功能是返回大于等于val的第一个数的指针,如果没找到,就返回集合的超尾。在这道题里面,就可以让val等于nums[i]-t,然后在集合中找到大于等于nums[i]-t的第一个数的位置,如果找不到,说明集合中的数都小于nums[i]-t,自然就说明集合中没有符合条件的数了;如果找到了,那么还要判断一下找到的数是否还不超过nums[i]+t,如果超过了那也不行的。

        找到符合范围的数之后,那么就应该要确定找到的数的索引范围了。如何确定索引范围呢?前面的数是在集合中找到的,要想确定集合中数的索引范围,那么就只需要一直保持集合中的元素个数为k个。为什么呢?举个例子,当遍历到nums[i]时,集合中的数有k个,这k个就是[i-k,i-1]间的数,这样就保证了在集合中找出的数的索引一定是在[i-k,i-1]间的,这样就肯定符合条件了。

        那为什么只考虑i的前面k个而不考虑i的后面k个呢?这是因为即使在i的后面k个元素中存在符合要求的nums[m],当遍历到nums[m]的时候,m的前面k个元素必定又包含有i,这样还是可以将nums[m]和nums[i]都找出来。又何必浪费时间呢?

        因此,当遍历到nums[i]的时候,如果集合中的元素多于k个,就应当删去不可能符合条件的nums[i-k-1]。

        还需要注意的是,由于题目并未限定t的大小和正负,因此求差时可能会使数据溢出,因此要用到long long数据类型。

3.3 代码实现

bool containsNearbyAlmostDuplicate(vector<int>& nums, int k, int t) {
        
        set<long long>s;//s中存放当前元素的前面k个元素
        int len=nums.size();

        for(int i=0;i<len;i++)
        {
            auto p=s.lower_bound((long long)nums[i]-t);//找到大于等于nums[i]-t的第一个数
            
            /*如果能找到大于等于nums[i]-t的第一个数,
              并且找到的数小于等于nums[i]+t,
              说明在nums[i-k]~nums[i]中有满足条件的数*/

            if(p!=s.end()&&(*p)<=(long long)nums[i]+t)return true;  
            
            //插入当前数,作为下一个数的前k个数之一

            s.insert(nums[i]);  

            /*如果集合中的元素数量多于k个,说明最左边的元素已经超出了k范围,
              就将其擦除,从而保证集合中所有元素必定只有nums[i]~nums[i-k+1],
              作为下一轮nums[i+1]的前k个数*/

             if(s.size()>k)s.erase(nums[i-k]); 
        }
        return false;
        
        
    }

猜你喜欢

转载自blog.csdn.net/qq_28114615/article/details/86307198