23.<tag-数组和原地哈希>-lt.442. 数组中重复的数据 + lt.lt- 268. 丢失的数字 1

本文详细探讨了两种解决数组中重复数据的方法:原地哈希和标记法,并提供了相应的代码实现。此外,还分析了寻找丢失数字的算法,包括排序后遍历查找和原地哈希策略。同时,提到了适用于不同重复次数的解题思路。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

442. 数组中重复的数据

[案例需求]
在这里插入图片描述

[思路分析一, 原地哈希]

  • 剑指Offer.03题目基本一致;
  • 原地哈希是依靠数组建立起index到nums[index]的映射, 从而可以很方便的根据数组的index得到固定的值;
  • 注意, 本题目中, 数字的有效范围在 [1, n] ;
  • 由于数组中的index是从0开始的, 所以我们的数组为: nums[0] =1, nums[1] = 2, 即 nums[index] = index + 1;
  • nums[index] 应该放置的位置为 int targetIndex = nums[index] - 1;

[代码实现]

class Solution {
    public List<Integer> findDuplicates(int[] nums) {
        //原地哈希
        // 对应的值为 nums[index] = index+1;
        // index = 0, nums[index] = index + 1 = 1 = num[0]

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

        for(int i = 0; i < nums.length; i++){
            while(i + 1 != nums[i]){
                swap(nums, i, nums[i] - 1);
                
                int targetIndex = nums[i] - 1; //理应待在的地方

                if(nums[i] == nums[nums[i] - 1]){
                    break;
                }
            }
        }

        for(int i = 0; i < nums.length; i++){
            if(i != nums[i] - 1)list.add(nums[i]);
        }
        return list;
    }

    public void swap(int[] nums, int left, int right){
        int temp = nums[left];
        nums[left] = nums[right];
        nums[right] = temp;
    }
}

[思路分析二, 标记法]

  • 遍历这个数组, 我们把遇到的数-1, 作为数组的下标, 并把这个下标处的数变为负数
  • 因为nums数组中数字从1到n, 如果数字不重复, 下标肯定是不同的!!!
  • 如果遍历到了某个下标时, 这个数已经是负数了, 那肯定就是重复的了
class Solution {
    public List<Integer> findDuplicates(int[] nums) {
        //标记法
        List<Integer> list = new ArrayList<>();

        //index 处为 nums[index] - 1;
        // nums[index] 的是索引为 index + 1;

        for(int i = 0; i < nums.length; i++){
            int index = Math.abs(nums[i]) - 1; //把遍历到的数字-1 作为下标(打标记的位置下标), 
            //另外, 我们不关心遍历到的数字是否被打上标记, 我们只关心遍历结束, 所以对 nums[i]取了绝对值

            if(nums[index] < 0){
                // nums[index]处不是重复的数, 
                //而是说有另外的两个数是重复的, 他倆映射的索引位置是nums[index]这个数, 
                // nums[index] 为负数, 说明是有重复了
                list.add(Math.abs(index + 1));
            }else{
                nums[index] = -nums[index];
            }
        }

        return list;
    }
}

如果我们是遍历重复三次, 重复n次的某个数应该如何求解 ?

  • 基本思路类似, 都是打标记法(把遍历到的数作为数组的下标), 区别在于如何打标记的策略;
    在这里插入图片描述

lt- 268. 丢失的数字

[案例需求]
在这里插入图片描述

[思路分析一, 排序后遍历查找]

[代码实现]

 // //先排序, 再检查差 > 1的数
      class Solution {
    public int missingNumber(int[] nums) {
        Arrays.sort(nums);
        int len = nums.length;

        if(nums[0] != 0)return 0;

        for(int i = 1; i < len; i++){
            if(nums[i] - nums[i - 1] > 1)return nums[i - 1] + 1;
        }
        return len;
    }
}

[思路分析二, 原地哈希]

[代码实现]

class Solution {
    public int missingNumber(int[] nums) {
       //原地哈希

       int len = nums.length;

       for(int i = 0; i < nums.length; i++){
           int targetIndex = nums[i];

            if(i != targetIndex && nums[i] < len)
                swap(nums, i--, targetIndex);
       }


       for(int i = 0; i < len; i++){
           if(nums[i] != i)
                return i;
       }
       return len;
    }

    public void swap(int[] nums, int left, int right){
        int temp = nums[left];
        nums[left] = nums[right];
        nums[right] = temp;
    }
}

其他解法待补充: dbc

[思路分析三, 位运算法]

在这里插入图片描述


class Solution {
    public int missingNumber(int[] nums) {
        int n = nums.length;
        int ans = 0;
        for (int i = 0; i <= n; i++) ans ^= i;
        for (int i : nums) ans ^= i;
        return ans;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值