LeetCode 数组中重复数字

在LeetCode中有四道查找数组中重复数字的类似的题目,先总结一波:
LeetCode 217:Contains Duplicate
LeetCode 219:Contains Duplicate II
LeetCode 220:Contains Duplicate III
LeetCode 287:Find the Duplicate Number

一. Contains Duplicate

给定一个整数数组,判断该数组是否存在重复元素;当数组中某元素至少存在两个,则返回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

解题思路:这个就很简单,主要思路有:

(1)将数组排序,判断前后两个元素是否一样,如果一样则返回true,否则返回false;
(2)方法(1)对数组进行排序,改变了数组元素的位置;若要求不能修改数组元素,可以创建一个辅助HashSet,判断HashSet中是否已经存在该元素,存在则返回true,否则返回false,并将其加入在HashSet中。

代码实现:
class Solution {
    public boolean containsDuplicate(int[] nums) {
        int len = nums.length;

        if(len==0||len==1)
            return false;
        //方法二,借助HashSet
        HashSet<Integer> re = new HashSet<Integer>();
        for(int i = 0;i<len;i++){
            if(re.contains(nums[i]))
                return true;
            re.add(nums[i]);
        }

        /*
        方法一:首先将数组排序
        Arrays.sort(nums);

        for(int i = 0;i<len-1;i++){
            if(nums[i]==nums[i+1])
                return true;
        }*/

        return false;
    }
}

二. Contains Duplicate II

给定一个数组nums和一个整数k,是否存在两个不相等的整数 i 和 j,使得nums[i] == nums[j],并且i和j之间的距离最多为k。

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

范例2:
输入:[1,0,1,1], k = 1
输出: true

范例3:
输入:[1,2,1], k = 0
输出: false

解题思路:

这道题在题目一的基础上增加了k,使得需要满足条件|i-j| <=k;
那么可以借助HashMap,key为nums[i],value为i,用来记录数组元素以及在数组中的位置;
那么当num[j]已经在HashMap中时,即nums[i] == nums[j],那么判断是否满足 j-i<=k,如果满足,则直接返回true;如果不满足,则需要用(nums[j],j)替换(nums[i],i),因为之后可能还会出现元素等于nums[i],如范例2;

代码实现:
class Solution {
    public boolean containsNearbyDuplicate(int[] nums, int k) {
        int len = nums.length;
        if(len<=1||K==0){
            return false;
        }

        HashMap<Integer, Integer> temp = new HashMap<Integer, Integer>();

        for(int i = 0;i<len;i++){
            if(temp.containsKey(nums[i])&&(i-temp.get(nums[i]))>k){
                temp.put(nums[i], i);
            }else if(temp.containsKey(nums[i])&&(i-temp.get(nums[i]))<=k)
                return true;
            else
                temp.put(nums[i], i);
        }

        return false;
    }

}

三. Contains Duplicate III

给定整数数组,是否存在两个不相等的 i 和 j 使得nums[i] 和nums[j] 的差值至多为 t ,i 和 j的距离至多为k。

范例1:
输入: [1,2,3,1], k = 4, t = 0
输出: true

范例2:
输入: [1,0,1,1], k = 1, t = 0
输出: true

范例 3:
输入: [4,2], k = 2, t = 1
输出: false

解题思路:

利用滑动窗口和TreeSet中的floor()方法和ceiling()方法;
滑动窗口:将TreeSet中的元素数量控制到最多为k个,使得|i-j|<=k成立;
floor()方法返回TreeSet中小于等于nums[i]的元素中的最大值,如果TreeSet中不存在num[i],则返回null;
ceiling()方法返回TreeSet大于等于nums[i]的元素中的最小值,如果不存在nums[i],则返回null;

代码实现:
class Solution {
    public boolean containsNearbyAlmostDuplicate(int[] nums, int k, int t) {
        int len = nums.length;
        if(len<=1 || k<=0)
            return false;

        TreeSet<Integer> temp = new TreeSet<Integer>();

        for(int i = 0;i<len;i++){
            //找到小于等于nums[i]的元素中的最大值
            if(temp.floor(nums[i])!=null){
                long farMin = temp.floor(nums[i]);
                if(nums[i]-farMin<=t)
                    return true;
            }
            //找到大于等于nums[i]的元素中的最小值
            if(temp.ceiling(nums[i])!=null){
                long leastMax = temp.ceiling(nums[i]);
                if(leastMax - nums[i]<=t)
                    return true;
            }

            temp.add(nums[i]);

            //设置treeset中的元素数量最多为k个
            if(temp.size()>k){
                temp.remove(nums[i-k]);
            }
        }

        return false;

    }
}

四. Contains Duplicate III

给定一个包含n+1个整数的数组,其中每个整数的范围为1~n,判断是否至少存在一个重复元素,并找到该元素,假设该数组中只存在一个重复元素。

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

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

注意:
不能修改数组,该数组为只读数组;
空间复杂度为O(1);
时间复杂度小于(n^2);
该数组中只存在一个重复元素,但该元素的个数超过2个;

解题思路:

首先忽略注意中的几个条件,解决方案有:
(1)如题目一中的解法,对数组进行排序,或者创建一个额外的hashset,然后进行判断即可;
(2)因为数组中元素的范围为1~n,那么数组排序后,若不存在重复的元素,则数字 i 一定出现在下标为 i 的位置上(这里假设 下标从1开始);若存在重复元素,那么下标为i的位置上可能不是数字i;
所以对数组进行重排序,如果位置i上的元素为i,那么继续扫描,如果不为i,则和nums[i]位置上的元素进行比较,如果和nums[i]位置上的元素相等,则找到重复元素,如果不相等,将和nums[i]位置上的元素进行交换,即将nums[i]放置到i上,接下来重复比较交换的过程,直到找到重复的元素;

接着看注意中的几个条件,不能改变数组元素,并且空间复杂度为O(1),那么这就是说不能重排数组以及借助额外的辅助空间;
时间复杂度要小于O(n^2),那么直接暴力破解也不行;

所以这道题目的解决方案为:使用二分法的思想。因为数组中的元素大小范围为1~n,那么我们将其该范围从正中间分成两个部分,1~m和m+1~n,那么该数组中的元素在1~m范围内的元素个数,如果大于m,那么重复元素一定存在于前半部分,否则存在在后半部分;接着继续一分为二进行判断,直到找打重复元素位置

代码实现:
class Solution {
    public int findDuplicate(int[] nums) {
        int len = nums.length;

        int start = 1;
        int end = len;

        int half = 0;

        while(start<end){
            int middle = start + (end-start)/2;
            half = count(nums,start,middle);

            if(half > middle-start+1){
                end = middle;
            }else{
                start = middle+1;
            }
        }

        if(start==end){
            half = count(nums,start,end);
            if(half>1)
                return start;
        }

        return -1;

    }
    //计算数组中大小范围为start~end的元素个数
    public int count(int[] nums, int start, int end){
        if(start>end)
            return 0;
        int re = 0;
        for(int i = 0;i<nums.length;i++){
            if(nums[i]>=start&&nums[i]<=end){
                re ++;
            }
        }
        return re;

    }
}

时间复杂度为O(nlogn),空间复杂度为O(1)

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值