【每日一题】LeetCode. 287. 寻找重复数

每日一题,防止痴呆 = =

一、题目大意

给定一个包含 n + 1 个整数的数组 nums,其数字都在 1 到 n 之间(包括 1 和 n),可知至少存在一个重复的整数。假设只有一个重复的整数,找出这个重复的数。
在这里插入图片描述
在这里插入图片描述
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/find-the-duplicate-number

二、题目思路以及AC代码

这道题刚看到的时候以为是用异或做了 = =,结果发现题目中说重复数的重复次数可能大于1次,这用异或就没有办法了,主要还是参照题解学习了三种解题方式。

思路一:二分

题目要求时间复杂度小于O(n2),所以无非就是O(nlogn)和O(n),一看到O(nlogn),我首先是想到快排,然后稀里糊涂就给过了(其实题目要求不能修改原数组的),所以就可以考虑二分。

但是对于二分,我们需要构造单调序列,这里可以考虑单调序列cnt,其中cnt[i]表示在nums数组中,小于等于i的数的个数,我们用target表示我们要找的重复数,那么可以想象当i < target的时候,cnt[i] 是小于等于i的(当且仅当重复数没有占用小于target数的位置时等号成立),当i >= target的时候,cnt[i]是大于i的,所以我们就可以根据以上情况进行二分,即如果cnt[mid] <= mid,那么target一定在右侧的数组中,如果cnt[mid] > mid,那么target一定在左侧的数组中或者就是mid.

按道理来说,应该先计算cnt数组,复杂度是O(n),然后再二分,复杂度是O(logn),总体复杂度O(n),但是由于题目要求不能使用额外的存储空间,所以这里就得现算cnt,造成结果的复杂度是O(nlogn).

思路二:二进制

和思路一差不多,这里的思路是考虑确定重复数的各个二进制位。即我们考虑重复数的第i位是否是1,我们令x表示nums数组中,第i位为1的总个数,y表示1~n中,第i位为1的总个数。那么如果x > y,那该位就是1,如果x <= y,那么该位就是0.

我们可以分情况考虑上述的问题。重复数我们分为重复1次,和重复多次。如果是重复一次,那么当重复数第i位为1的时候,则x = y + 1,当重复数为0的时候,则x = y,满足上述提到的条件。然后当重复数是重复多次的时候,必定要替代一个原来的数B,如果重复数第i位为1,B第i位为0,那么x > y+1,如果重复数第i位为1,B第i位为1,那么x = y+1 > y,如果重复数第i位为0,B第i位为1,那么x < y,如果重复数第i位为0,B第i位为0,那么x = y,综上所述,都满足上述提到的判定重复数第i位值得条件,即如果x > y,那该位就是1,如果x <= y,那么该位就是0.

思路三:判环

该题同样可以建立成一个图中判环的问题。我们令i -> nums[i]为边建图,那么由于有重复数的原因,所以势必有两个顶点同时指向同一个顶点,此时有环出现,而环的起点就是我们要找的重复数。

可以使用快慢指针来实现。

AC代码

上述代码我实现了一下思路一和思路三,列在下面。

思路一:

class Solution {
public:
    int findDuplicate(vector<int>& nums) {
        int n_size = nums.size();
        int l = 1, r = n_size - 1;
        int mid = (l + r) >> 1;

        while (l < r) {
            mid = (l + r) >> 1;

            int x = 0;
            for (int i=0;i<n_size;i++) {
                if (nums[i] <= mid) {
                    x++;
                }
            }

            if (x <= mid) l = mid + 1;
            else r = mid;
        }

        return l;
    }
};

思路三:

class Solution {
public:
    int findDuplicate(vector<int>& nums) {
        int slow = 0, fast = 0;
        do {
            slow = nums[slow];
            fast = nums[nums[fast]];
        } while(slow != fast);

        slow = 0;
        while (slow != fast) {
            slow = nums[slow];
            fast = nums[fast];
        }

        return slow;
    }
};

如果有问题,欢迎大家指正!!!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值