[剑指offer]面试题3+:数组中的重复元素

27 篇文章 0 订阅
6 篇文章 0 订阅

根据剑指offer数组中的重复元素,我在Leetcode上面找了几道类似的题进行练习。

1.删除排序数组中的重复项I(Leetcode.26)

给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。

不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。

示例 1:

给定数组 nums = [1,1,2], 

函数应该返回新的长度 2, 并且原数组 nums 的前两个元素被修改为 1, 2。 

你不需要考虑数组中超出新长度后面的元素。
示例 2:

给定 nums = [0,0,1,1,1,2,2,3,3,4],

函数应该返回新的长度 5, 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4。

你不需要考虑数组中超出新长度后面的元素。

根据题目的意思,首先数组是排序的,其次是要原地删除所有的重复元素。

1.1直接使用vector的内置函数

直接定义好vector的迭代器,然后比较相邻元素,如果相同,则删除该元素,注意使用erase删除之后,迭代器会自动指向下一个元素。否则,就将迭代器++.

public:
    int removeDuplicates(vector<int>& nums) {
        vector<int>::iterator it;
        if(nums.size()==0)
            return nums.size();
        for(it=nums.begin();it!=nums.end()-1;)
        {//边界问题
            if(*it==*(it+1))
                nums.erase(it);
            else
                it++;
        }
        return nums.size();
        
    }
1.2 使用快慢指针

已知数组元素是有序的,我们可以设定两个指针 j , i j,i j,i j j j指向第0个元素, i i i指向第一个元素,如果 j j j i i i所指向的元素相等,那么就 i i i++,判断下一个元素是否相等。如果如果 j j j i i i所指向的元素不相等,那么就将 j j j++,并且使得 j j j当前指向的元素更新为 i i i所指向的元素。比如 1 , 1 , 2 , 3 {1,1,2,3} 1,1,2,3, j = 0 , i = 1 j=0,i=1 j=0,i=1起初,因为 n u m [ j ] = = n u m [ i ] num[j]==num[i] num[j]==num[i],因此我们将 i i i++,而 j j j依然不变。更新后, i = 2 i=2 i=2,num[2]!=num[0],将 j j j++,并且将用2放在当前位置,即num[1]=2.这里的原地删除的意思是我们只需要保证前len个元素是符合要求的,而len后面的不需要关心。因为输出是这样的:

// nums 是以“引用”方式传递的。也就是说,不对实参做任何拷贝
int len = removeDuplicates(nums);

// 在函数里修改输入数组对于调用者是可见的。
// 根据你的函数返回的长度, 它会打印出数组中该长度范围内的所有元素。
for (int i = 0; i < len; i++) {
    print(nums[i]);
}

public:
    int removeDuplicates(vector<int>& nums) {
        int j=0;
        if(nums.size()<2)
            return nums.size();
        for(int i=1;i<nums.size();i++)
        {
            if(nums[j]!=nums[i])
                nums[++j]=nums[i];
        }
        return ++j;
        
        
    }

2.删除排序数组中的重复项II(Leetcode.80)

给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素最多出现两次,返回移除后数组的新长度。

不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。

示例 1:

给定 nums = [1,1,1,2,2,3],

函数应返回新长度 length = 5, 并且原数组的前五个元素被修改为 1, 1, 2, 2, 3 。

你不需要考虑数组中超出新长度后面的元素。
示例 2:

给定 nums = [0,0,1,1,1,1,2,3,3],

函数应返回新长度 length = 7, 并且原数组的前五个元素被修改为 0, 0, 1, 1, 2, 3, 3 。

你不需要考虑数组中超出新长度后面的元素。

相比于第一道,这道题的要求是保留2个重复数字。也就是重复的定义改变了,2个以上才是重复。依然是排序数组,所以依旧可以使用快慢指针,只是需要额外增加一个计数器表示元素的数量。在判断 j j j i i i所指向的元素相等时,需要额外考虑数量是否达到2个,如果没有达到两个,就更新 j j j写入数组。在 j j j i i i所指向的元素不相等时,就表示有新元素了,更新 j j j并且将元素写入数组,更新计数器重新计数。

public:
    int removeDuplicates(vector<int>& nums) {
        int j=0;
        int k=1;
        if(nums.size()<=2)
            return nums.size();
        for(int i=1;i<nums.size();i++)
        {
            if(nums[j]==nums[i]&&k<2)
            {
                nums[++j]=nums[i];
                ++k;
            }  
            if(nums[j]!=nums[i])
            {
                 nums[++j]=nums[i];
                 k=1;
            }  
        }
        return ++j;
    }

3.存在重复元素I(Leetcode.217)

给定一个整数数组,判断是否存在重复元素。

如果任何值在数组中出现至少两次,函数返回 true。如果数组中每个元素都不相同,则返回 false。

示例 1:

输入: [1,2,3,1]
输出: true
示例 2:

输入: [1,2,3,4]
输出: false
示例 3:

输入: [1,1,1,3,3,4,3,2,4,2]
输出: true

这道题,数组不是有序的,如果想要使用快慢指针来判断,需要先排序。

public:
    bool containsDuplicate(vector<int>& nums) {
        sort(nums.begin(),nums.end());
        int n=nums.size();
        if(n<2)
            return false;
        int j=0;
        for(int i=1;i<nums.size();i++)
        {
            if(nums[j]!=nums[i])
                nums[++j]=nums[i];
        }
        if(n>(++j))
            return true;
        else
            return false;
    }

4.存在重复元素II(Leetcode.219)

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

示例 1:

输入: nums = [1,2,3,1], k = 3
输出: true
示例 2:

输入: nums = [1,0,1,1], k = 1
输出: true
示例 3:

输入: nums = [1,2,3,1,2,3], k = 2
输出: false

相比于上一道题,这一道题增加了一个约束条件,相同元素下标之间的距离。这就要求我们不可以随便排序,因为排序之后下标会发生变化。需要记录下标又需要比较元素,我们就想到采用map,建立一个map,使用数组元素作为键值,使用下标作为value。遍历数组,我们查询每一个元素是否在map中,如果在,那么二者下标的距离是否满足约束,如果满足返回true,如果不满足则更新下标为较大的下标。如果不在,则加入map.这里查找的时候使用STL中map查找函数find,如果找不到,则返回指向end的迭代器。查找的时间代价为 O ( l o g n ) O(logn) O(logn)

public:
    bool containsNearbyDuplicate(vector<int>& nums, int k) {
        int n=nums.size();
        map<int,int>hash_n;
        for(int i=0;i<n;i++)
        {
            if(hash_n.find(nums[i])!=hash_n.end())//exist
            {
                if(i-(hash_n.find(nums[i])->second)<=k)
                    return true;
                else
                    (hash_n.find(nums[i]))->second=i;
            }
            else
            {
                hash_n[nums[i]]=i;
            }    
        }
        return false;
    }

5.数组中的重复数据(Leetcode.442)

给定一个整数数组 a,其中1 ≤ a[i] ≤ n (n为数组长度), 其中有些元素出现两次而其他元素出现一次。

找到所有出现两次的元素。

你可以不用到任何额外空间并在O(n)时间复杂度内解决这个问题吗?

示例:

输入:
[4,3,2,7,8,2,3,1]

输出:
[2,3]

这道题是无序的,数组元素有范围。所以需要考虑如何标记已经遍历过的数组元素。
因此有下标范围,所以我们可以将数组视为一个哈希表。将遍历过的元素标记为其相反数。

public:
    vector<int> findDuplicates(vector<int>& nums) {
        //标记索引 使用a[i]作为索引
        vector<int>result;
        for(int i=0;i<nums.size();i++)
        {
            int nums_abs=abs(nums[i]);
            if(nums[nums_abs-1]>0)
                nums[nums_abs-1]*=-1;//标记为负数
            else
                result.push_back(abs(nums[i]));
        }
        return result;
    }

总结

1.数组是否有序:数组有序可以考虑快慢指针,数组无序又不在意下标可以排序再使用快慢指针。
2.数组元素是否有范围:数组元素有范围且可以用作下标,可以使用数组做hash表;数组元素没有范围可以使用map做hash表。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值