287. 寻找重复数

在这里插入图片描述
法一:抽屉法,放回原位,在放的过程中,如果自己不在原位,但是原位已经被自己的复制品霸占了,就直接返回当前数字,当前数字就是重复的,否则就交换,把当前数字放回原位。缺点就是要swap,会改变原数组.
法二:快慢指针,只要是有重复数字就一定存在环。这个环和链表的环差不多,比如31342这个数组,我们从0开始走,走到当前数组的某一位,就把那一位的值作为下一步要走的下标。0->3->4->2->3->4->2,这就进入了环。但是这里的快慢指针怎么走呢,如何体现快慢的思想,我们把快慢指针一开始都设为0,慢指针走的就是nums[slow],快指针走的就是nums[nums[fast]],这样就可以体现快慢的思想了,因为一定会有环,所以将它俩就初始化成这个,当相等时退出循环。
为了找出环的起点,就将slow重置为整个链表的起点0,然后快慢指针都是一步一步走,相等的地方就是环的起点,详情请见循环链表II

class Solution {
public:
    int findDuplicate(vector<int>& nums) {
        //抽屉法,放回原位,在放的过程中,如果自己不在原位,且原位已经被自己的复制品霸占了就返回当前数字
        //1放在0号位,放在比自己小1的位置上
        // for(int i = 0; i < nums.size(); ++i){
        //     while(nums[i] != i+1){
        //         if(nums[nums[i]-1] == nums[i]) return nums[i];
        //         swap(nums[i],nums[nums[i]-1]);
        //     }
        // }
        // return -1;

        //如何不修改数组,就是不能swap
        
        int slow = 0;
        int fast = 0;
        slow = nums[slow];
        fast = nums[nums[fast]];
        //因为一定有重复,所以一定有环
        while(slow != fast){
            slow = nums[slow];
            fast = nums[nums[fast]];
        }
        slow = 0;
        while(slow != fast){
            slow = nums[slow];
            fast = nums[fast];
        }
        return fast;
    }
};

法一:抽屉
法二:快慢指针,起点是0,不是nums[0]!!
法三:变种二分,每次找一个mid,然后遍历,维护一个cnt来记录小于等于mid的数,然后根据cnt来判断向左还是向右。
mid = (1 + 5) / 2 = 3 arr小于等于的3有4个(1,2,2,3),1到3中肯定有重复的值
mid = (1 + 3) / 2 = 2 arr小于等于的2有3个(1,2,2),1到2中肯定有重复的值
mid = (1 + 2) / 2 = 1 arr小于等于的1有1个(1),2到2中肯定有重复的值

class Solution {
public:
    int findDuplicate(vector<int>& nums) {
        // int slow = 0, fast = 0;
        // while(1){
        //     slow = nums[slow];
        //     fast = nums[nums[fast]];
        //     if(slow == fast) break;//要在内部判断,因为slow和fast一开始设为起点是一样的
        //     //一般快慢指针都是在里面判断的,因为起点都是一样的
        // }
        // slow = 0;
        // while(slow != fast){
        //     slow = nums[slow];
        //     fast = nums[fast];
        // }
        // return slow;

        //二分,每次找一个mid,然后遍历,维护一个cnt来记录小于等于mid的数,然后根据cnt来判断向左还是向右
        int l = 0, r = nums.size()-1;
        //不是具体找某个数,相等的时候退出
        while(l < r){
            //mid = (1 + 5) / 2 = 3 arr小于等于的3有4个(1,2,2,3),1到3中肯定有重复的值
            //mid = (1 + 3) / 2 = 2 arr小于等于的2有3个(1,2,2),1到2中肯定有重复的值
            //mid = (1 + 2) / 2 = 1 arr小于等于的1有1个(1),2到2中肯定有重复的值
            int mid = l + (r-l)/2;
            int cnt = 0;
            for(int x : nums){
                if(x <= mid) cnt++;
            }
            if(cnt <= mid) l=mid+1;
            else r=mid;
        }
        return l;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值