LeetCode 442 数组中重复的数据

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

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

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

示例:

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

输出:
[2,3]

解法一:
不使用额外的空间(python)
这个题目开头暗示了n的范围,所以可以加以利用,将元素转换成数组的索引并对应的将该处的元素乘以-1;

若数组索引对应元素的位置本身就是负数,则表示已经对应过一次;在结果列表里增加该索引的正数就行;

class Solution:
    def findDuplicates(self, nums: List[int]) -> List[int]:
        count = []
        for n in nums:
            if nums[abs(n)-1]>0:
                nums[abs(n)-1] *=-1
            else:
                count.append(abs(n))
        return count

解法二:(java)
抽屉原理+异或运算
思路分析:“桶排序”的思想是“抽屉原理”,即“一个萝卜一个坑”,8 个萝卜要放在 7 个坑里,则至少有 1 个坑里至少有 2 个萝卜。

这里由于数组元素限定在数组长度的范围内,因此,我们可以通过一次遍历:

让数值 1 就放在索引位置 0 处;
让数值 2 就放在索引位置 1 处;
让数值 3 就放在索引位置 2 处;

……

一次遍历以后,那些“无处安放”的元素就是我们要找的“出现两次的元素”。

为了不使用额外的空间,这里使用到的一个技巧是“基于异或运算交换两个变量的值”:交换两个整数,除了引入一个新的变量,写出一个“轮换”的赋值表达式以外,还有两种比较 tricky 的做法,下面给出结论。

“异或运算”是不进位的二进制加法,它有如下性质:

如果 a ^ b = c ,那么 a ^ c = b 与 b ^ c = a 同时成立,利用这一条,可以用于交换两个变量的值。

于是,交换两个变量的值,例如 a 和 b,不使用第三个变量,有两种不同的方法:
在这里插入图片描述

class Solution {
    public List<Integer> findDuplicates(int[] nums) {
        List<Integer> res = new ArrayList<>();
        int len = nums.length;
        if (len == 0) {
            return res;
        }
        for (int i = 0; i < len; i++) {
            while (nums[i] <= len && nums[nums[i] - 1] != nums[i]) {
                swap(nums, i, nums[i] - 1);
            }
        }
        for (int i = 0; i < len; i++) {
            if (nums[i] - 1 != i) {
                res.add(nums[i]);
            }
        }
        return res;
    }
    private void swap(int[] nums, int index1, int index2) {
        if (index1 == index2) {
            return;
        }
        nums[index1] = nums[index1] ^ nums[index2];
        nums[index2] = nums[index1] ^ nums[index2];
        nums[index1] = nums[index1] ^ nums[index2];
    }
}

解法三:(c++)

遍历数组,元素作为索引对应位置加n
遍历数组,出现两次,则对应位置大于2n

class Solution {
public:
    vector<int> findDuplicates(vector<int>& nums) {
        vector<int> res;
        if(nums.empty()) return res;
        int n=nums.size();
        for(int i=0;i<n;i++)
        {
            int index=(nums[i]-1)%n;//对n取余,防止数字越界
            nums[index]+=n;         //对应位置加n
        }
        for(int i=0;i<n;i++)
        {
            //出现两次,则对应位置大于2n
            if(nums[i]>2*n)
                res.push_back(i+1);
        }
        return res;
    }
};
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值