寻找重复数

描述

给定一个包含 n + 1 个整数的数组 nums,其数字都在 1 到 n 之间(包括 1 和 n),可知至少存在一个重复的整数。假设只有一个重复的整数,找出这个重复的数。

示例 1:

输入: [1,3,4,2,2]
输出: 2
示例 2:

输入: [3,1,3,4,2]
输出: 3
说明:

不能更改原数组(假设数组是只读的)。
只能使用额外的 O(1) 的空间。
时间复杂度小于 O(n2) 。
数组中只有一个重复的数字,但它可能不止重复出现一次。

解析

若可以移动元素

如果数组可以移动元素,解法很多。比如,

先排序再比较;

hash;

抽屉原理

链表有环双指针

使用环形链表II的方法解题(142.环形链表II),使用142题的思想来解决此题的关键是要理解如何将输入的数组看作为链表。
首先明确前提,整数的数组 nums中的数字范围是[1,n]。考虑一下两种情况:

如果数组中没有重复的数,以数组[1,3,4,2]为例,我们将数组下标n和数nums[n]建立一个映射关系f(n),
其映射关系n->f(n)为:
0->1
1->3
2->4
3->2
我们从下标为0出发,根据f(n)计算出一个值,以这个值为新的下标,再用这个函数计算,以此类推,直到下标超界。这样可以产生一个类似链表一样的序列。
0->1->3->2->4->null

如果数组中有重复的数,以数组[1,3,4,2,2]为例,我们将数组下标n和数nums[n]建立一个映射关系f(n),
其映射关系n->f(n)为:
0->1
1->3
2->4
3->2
4->2
同样的,我们从下标为0出发,根据f(n)计算出一个值,以这个值为新的下标,再用这个函数计算,以此类推产生一个类似链表一样的序列。
0->1->3->2->4->2->4->2->……

综上
1.数组中有一个重复的整数 <==> 链表中存在环
2.找到数组中的重复整数 <==> 找到链表的环入口

至此,问题转换为142题。那么针对此题,快、慢指针该如何走呢。根据上述数组转链表的映射关系,可推出
142题中慢指针走一步slow = slow.next ==> 本题 slow = nums[slow]
142题中快指针走两步fast = fast.next.next ==> 本题 fast = nums[nums[fast]]

代码

类似抽屉原理

public int findDuplicate(int[] nums) {
        for (int i = 0; i < nums.length; i++) {
            if (nums[i] != i + 1) {
                if (nums[nums[i] - 1] == nums[i]) {
                    return nums[i];
                }
                swap(nums, i, nums[i] - 1);
                
                 if (nums[i] != i + 1) {
                    if (nums[nums[i] - 1] == nums[i]) {
                        return nums[i];
                    }
                    swap(nums, i, nums[i] - 1);
                }
            }
        }
        for (int i = 0; i < nums.length; i++) {
            if (nums[i] != i + 1) {
                return nums[i];
            }
        }
        return -1;
    }
    
    public void swap(int[] nums, int a, int b) {
        if (a == b) {
            return;
        }
        nums[a] = nums[a] ^ nums[b];
        nums[b] = nums[a] ^ nums[b];
        nums[a] = nums[a] ^ nums[b];
    }

链表有环双指针

public int findDuplicate(int[] nums) {
        int slow = 0;
        int fast = 0;
        slow = nums[slow];
        fast = nums[nums[fast]];
        while(slow != fast){
            slow = nums[slow];
            fast = nums[nums[fast]];
        }
        int pre1 = 0;
        int pre2 = slow;
        while(pre1 != pre2){
            pre1 = nums[pre1];
            pre2 = nums[pre2];
        }
        return pre1;
    }

 

posted on 2019-12-23 16:31  反光的小鱼儿  阅读(201)  评论(0编辑  收藏  举报