L442. 数组中重复的数据

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

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

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

示例:

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

输出:
[2,3]

1.利用hashMap实现计数的功能

class Solution {
    public List<Integer> findDuplicates(int[] nums) {
        int len = nums.length;
        HashMap<Integer, Integer> map = new HashMap<>();
        for(int num : nums){
            map.put(num, map.getOrDefault(num, 0)+ 1);
        }
        List<Integer> list = new ArrayList<>();
        for(int num: nums){
            if(map.get(num) == 2 && !list.contains(num)){
                list.add(num);
            }
        }
        return list;
    }
}

超时,首先,map遍历方式不对,这样超时是必然的

class Solution {
    public List<Integer> findDuplicates(int[] nums) {
        int len = nums.length;
        HashMap<Integer, Integer> map = new HashMap<>();
        for(int num : nums){
            map.put(num, map.getOrDefault(num, 0)+ 1);
        }
        List<Integer> list = new ArrayList<>();
        Set<Integer> set = map.keySet();//对set遍历即可
        for(Integer num : set){
            if(map.get(num) == 2){
                list.add(num);
            }
        }
        return list;
    }
}

2.无穷交换方法
就是把所有的都归位,剩下没归位的就是重复的,要满足条件,数字在1-n之间

class Solution {
    public List<Integer> findDuplicates(int[] nums) {
        int len = nums.length;
        List<Integer> list = new ArrayList<>();
        if(len == 0) return list;
        //仍是自造哈希表
        for(int i = 0; i < len; i++){
            while(nums[nums[i] - 1] != nums[i]){//通过该操作,将所有数字都归位
                //注意对于那些重复的数字,在第一次碰到的时候,会被调整的对的位置,第二次就不会调整了
                //此时nums[nums[i] - 1] == nums[i]
                //交换,不占用空间,利用异或
                swap(nums, i, nums[i] - 1);
            }
        }
        //对于所有不在应有的位置,加入list
        for(int i = 0; i < len; i++){
            if(nums[i] - 1 != i){
                list.add(nums[i]);
            }
        }
        return list;
    }
    void swap(int[] num, int i, int j){
        //注意特殊情况,i, j对应相同的值,特判
        if(i== j) return;
        num[i] = num[i] ^ num[j];
        num[j] = num[i] ^ num[j];
        num[i] = num[i] ^ num[j];
		
		//交换也可使用下面的方法:
		/*int tmp = nums[i];
        nums[i] = nums[nums[i] - 1];
        nums[tmp - 1] = tmp;//这里一定要使用tmp,中间有值变动
        */
    }
}

3.抽屉法,利用负数标记法
遍历,每一个数-1作为index下标,对应位置*-1,同时记得最后要修改过来
找到数字i时,将位置i-1处的数字翻转为负数。
如果位置i-1 上的数字已经为负,则i是出现两次的数字。

关于负数标记法,常用的为L448,那种方法可以标记没出现的数字,因此这里要换一种方法:

class Solution {
    public List<Integer>findDuplicates(int[] nums) {
        int len = nums.length;

        List<Integer> list = new ArrayList<>();

        if(len == 0) return list;

        for(int i= 0; i < len; i++){
            //int index = Math.abs(nums[i] - 1);//别忘了取绝对值,这里写错了
            int index = Math.abs(nums[i]) - 1;

            if(nums[index] < 0){
                list.add(index + 1);
                //已经被标记了,说明当前的index已经出现过来,反推回来,index+1
            }

            nums[index] = -nums[index];//先标记
        }
        return list;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值