LeetCode Top 100 Liked Questions 287. Find the Duplicate Number (Java版; Medium)

welcome to my blog

LeetCode Top 100 Liked Questions 287. Find the Duplicate Number (Java版; Medium)

题目描述
Given an array nums containing n + 1 integers where each integer is between 1 and n (inclusive), 
prove that at least one duplicate number must exist. Assume that there is only one duplicate number, 
find the duplicate one.

Example 1:

Input: [1,3,4,2,2]
Output: 2
Example 2:

Input: [3,1,3,4,2]
Output: 3
Note:

You must not modify the array (assume the array is read only).
You must use only constant, O(1) extra space.
Your runtime complexity should be less than O(n2).
There is only one duplicate number in the array, but it could be repeated more than once.
二分法; 核心:对[1,n]这个范围进行二分, 并不是对nums二分
class Solution {
    public int findDuplicate(int[] nums) {
    	//核心:对[1,n]这个范围进行二分, 并不是对nums二分
        int left = 1, right = nums.length-1, mid;
        while(left < right){
            mid = left + (right - left)/2;
            int count = 0;
            for(int a : nums){
                if(a<=mid){
                    count++;
                }
            }
            //如果小于等于mid的数没有重复的话, 那么count==mid;此时说明重复的数字出现在[mid+1,n]中
            //如果小于等于mid的数有重复的话, 那么count>mid
            if(count>mid){
                right = mid;
            }else{
                left = mid+1;
            }
        }
        return left;
    }
}

第二次做; 核心: 1) 当成单链表的题, 怎么表示next指针? arr[i]当做i的next指针, 或者说i的next是arr[i] 2) 单链表中环的入口节点就是重复出现的值, 所以找到换的入口节点即可; 证明过程中发现: 快慢指针相遇需要的步数是环中节点个数的整数倍, 可以当成个性质记住

数组变成列表的例子

//一共n+1个数字,每个数字的取值范围{1,2,...,n}, 只有一个数字重复出现;  arr[i]作为i的next指针, 或者说i的next是arr[i]
class Solution {
    public int findDuplicate(int[] nums) {
        int slow=nums[0], fast=nums[0];
        //寻找快慢指针相遇的节点
        do{
            slow = nums[slow];
            fast = nums[fast];
            fast = nums[fast];
        }while(slow != fast);
        fast = nums[0];
        while(fast!=slow){
            slow = nums[slow];
            fast = nums[fast];
        }
        return slow;
    }
}
第一次做; 难点:不能改变原数组!; 把数组看成链表, 转化成链表的问题: 寻找链表中环的入口节点; 环的入口节点就是重复出现的值, 为什么? 因为nums[index]看成next指针, 出现环说明至少有两个元素的值相等, 也就指向了同一个地方
class Solution {
    public int findDuplicate(int[] nums) {
        //数组长度必须大于等于2
        /*
        最核心:
        index看成节点引用 
        nums[index]看成节点的val
        nums[index]还要看成next指针
        */
        int left=0, right=nums[0];
        while(nums[left] != nums[right]){
            //慢指针一次走一步
            left = nums[left];
            //快指针一次走两步; 可以写成一句right = nums[nums[right]
            right = nums[right];
            right = nums[right];
        }
        //
        right = nums[right];
        left = 0;
        while(nums[left] != nums[right]){
            left = nums[left];
            right = nums[right];
        }
        return nums[left];
    }
}
环的入口节点就是重复出现的值, 正是因为某个值重复出现了, 所以又重新指向了该值, 类似单链表中的环; LHS(Left-Hand Side)表示等式的左边; RHS(Right-Hand Side)表示等式的右边; 环入口节点到相遇的节点距离设为a; 比我之前的证明更简洁

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值