LeetCode287之寻找重复数(相关话题:位运算,抽屉原理)

目录

题目描述

解法一:抽屉原理

思路一

思路二

解法二:位运算

解题总结


题目描述

给定一个整数数组 a,其中1 ≤ a[i] ≤ n (n为数组长度), 其中有些元素出现两次而其他元素出现一次。找到所有出现两次的元素。你可以不用到任何额外空间并在O(n)时间复杂度内解决这个问题吗?

示例:

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

输出:
[2,3]

解法一:抽屉原理

思路一

仔细观察题目描述 1 ≤ a[i] ≤ n (n为数组长度),数组里的不同的元素类别可视为抽屉。数组个数视之为苹果

1.从头到位遍历数组如果发现 i==(a[i]-1),说明a[i]处的数据是对的,直接跳过。

2. i!=(a[i]-1)的话再判断a[a[i]-1]==a[i]是否成立,成立的话说明a[i]出现过2次,直接放入结果

3.a[a[i]-1]!=a[i]的话交换下标a[i]-1和i处的数据,交换之后可能导致a[i]-1>i,这时i可以不用继续向前移动,a[i]-1<=i的话要继续移动i

结合具体示例理解上述算法描述
class Solution {
    public List<Integer> findDuplicates(int[] nums) {

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

        int n = nums.length;
        for( int i =0;i<n;i++ ){
            //索引 和 (数-1) 对应了
            if( nums[i]-1==i ){
                continue;
            }
            int curVal = nums[i];
            // 没有对应的时候
            // 如果当前值curVal 所对应的索引curVal-1处的值nums[curVal-1]相等,说明这个值出现了两次
            if( nums[curVal-1] == curVal ){
                ans.add(curVal);
            }else{
                //如果不对应,交换两处的值
                nums[i] = nums[curVal-1] ;
                nums[curVal-1] = curVal;       
                //由于是向前遍历,当 i > curVal-1 时,说明curVal-1索引处的值被处理过,这里直接交换
                // 当i<curVal-1时,curVal-1索引处的值没有处理过,先交换
                // 交换之后继续对当前值处理,由于循环会i++;因此这里i--;使得下一次循环依然在原地处理
                if( i<curVal-1 ){
                    i--;
                }
            }
        }
        return ans;
    }
}

思路二

把下标index和下标nums[index]处的数位互换,数组重复或则之前已经有序的下标结束循环

​
class Solution {

    public int findDuplicate(int[] nums) {
        
        int index = 0;

        while(index < nums.length){

           if(nums[index]-1!= index){

        	   //这种情况构成循环所以要额外判断
               if(nums[index] == nums[nums[index]-1]){
                   return nums[index];
               }
               //index之前的下标已经有序,再次出现小于的元素可认为是重复
               if(nums[index]-1 < index){
                   return nums[index];
               }
  
               swap(nums,nums[index]-1,index);

           }else {
               index++;
           }

        }
       return 0;
   }

   private void swap(int[] nums,int i,int j){

       int tmep = nums[i];
       nums[i] = nums[j];
       nums[j] = tmep;
 
   }
}

​

解法二:位运算

 [1,n]的二进制中的各位是1的数次是固定的,比如

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

[1, 4]的位数如下

从上面我们可以知道,如果第i位1的总数x > y,那么重复数的第i位就是1;

class Solution {
    public int findDuplicate(int[] nums) {
        int n = nums.length, ans = 0;
        int bit_max = 31;
        while (((n - 1) >> bit_max) == 0) {
            bit_max -= 1;
        }
        for (int bit = 0; bit <= bit_max; ++bit) {
            int x = 0, y = 0;
            for (int i = 0; i < n; ++i) {
                //把nums[1]-nums[n]的第bit位为1的挨个计算一遍
                if ((nums[i] & (1 << bit)) != 0) {
                    x += 1;
                }
                //把1-n的第bit位为1的挨个计算一遍
                if (i >= 1 && ((i & (1 << bit)) != 0)) {
                    y += 1;
                }
            }
            if (x > y) {
                ans |= 1 << bit;
            }
        }
        return ans;
    }
}

解题总结

掌握解题的思想之后代码只是实现思想的工具,前期需要大量储备各种思想

另外本题还有二分和快慢指针的求解方法

LeetCode287之寻找重复数(相关话题:二分查找,快慢指针)_leetcode寻找重复记录_数据与后端架构提升之路的博客-CSDN博客

相似题目

LeetCode645错误的集合 
(参考题解-理解透彻版)

LeetCode448找到所有数组中消失的数字

LeetCode442数组中重复的数据

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

数据与算法架构提升之路

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值