【LeetCode】Day137-寻找消失&重复数

题目1、找到所有数组中消失的数字

448. 找到所有数组中消失的数字【简单】

题解

如果用一个哈希表对数字进行计数,之后统计出现次数为0的数字是很容易的,但是如何用O(n)时间复杂度不使用额外空间来解决此题呢?
提示里说利用输入数组完成,还是没想到怎么做,于是看了官解。

由于nums的数字范围都在[1,n]中,我们可以利用这一范围之外的数字,来表示该数是够存在。

具体做法就是遍历nums,每遇到一个数字x,就让nums[x-1]增加n,最后找谁没大于n,就是没存在过。

至于为什么让x-1下标+n而不是x下标呢?因为nums数组中数组范围在1~n,而数字%n的余数范围在0~n-1,所以需要让下标映射上

class Solution {
    public List<Integer> findDisappearedNumbers(int[] nums) {
        List<Integer>res=new ArrayList<>();
        int n=nums.length;
        for(int i=0;i<n;i++){
            int x=(nums[i]-1)%n;
            nums[x]+=n;
        }
        for(int i=0;i<n;i++){
            if(nums[i]<=n)
                res.add(i+1);
        }
        return res;
    }
}

时间复杂度: O ( n ) O(n) O(n)

空间复杂度: O ( 1 ) O(1) O(1)

题目2、寻找重复数

287. 寻找重复数【中等】

题解

和上一题类似,如果用一个哈希表对数字进行计数,之后统计出现次数为0的数字是很容易的,但是如何不修改数组 nums不使用额外空间来解决此题呢?

二分法

定义cnt[i] 为nums数组中<=i 的数有多少个,以[1,3,4,2,2]为例,列出每个数字的cnt值:
在这里插入图片描述
可以发现cnt是单调递增的,因此我们可以利用二分法来解题,对1~n进行二分法查找,找到cnt[i]>nums[i]的最小 i

class Solution {
    public int findDuplicate(int[] nums) {
        int n=nums.length;
        int l=1,r=n,res=-1;
        //二分法遍历数字1~n
        while(l<=r){
            int cnt=0,mid=(l+r)/2;
            //找小于mid的数
            for(int i=0;i<n;i++){
                if(nums[i]<=mid)
                    cnt++;
            }
            if(cnt<=mid)
                l=mid+1;
            else{
                r=mid-1;
                res=mid;
            }
        }
        return res;
    }
}

时间复杂度: O ( n l o g n ) O(nlogn) O(nlogn)

空间复杂度: O ( 1 ) O(1) O(1)

快慢指针法

对nums数组建图,每个位置 i 连一条 i->nums[i]边,如下图所示:
在这里插入图片描述
由于存在重复数字,那么这个位置一定至少有两条指向他的边,因此一定存在环,我们要找到的就是环的入口,等价于142. 环形链表 II 问题,使用快慢指针解决。

  • 判断有环?
    快指针每次走两步,慢指针每次走一步,能相遇就有环。

  • 入环点在哪?
    找到相遇位置后,固定 fast 指针,slow 指针从头结点开始,两个人一步一步走,相遇点即入环点。(可以证明)

class Solution {
    public int findDuplicate(int[] nums) {
        int n=nums.length;
        int slow=0,fast=0;
        //slow走一步,fast走两步
        do{
            slow=nums[slow];
            fast=nums[nums[fast]];
        }while(slow!=fast);
        //找入环点
        slow=0;
        while(slow!=fast){
            slow=nums[slow];
            fast=nums[fast];
        }
        return slow;
    }
}

时间复杂度: O ( n ) O(n) O(n)

空间复杂度: O ( 1 ) O(1) O(1)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值