数字中重复的数字(不修改数组且不开辟新空间)

2021-03-02 19:27

阅读:539

标签:mic   int   判断   抽屉   存在   技术   nlog   不可   接下来   

之前做过这样的题,,之前的题是可以修改数组的,那么如果不可以修改数组,我们如何来做这个题呢?

1.题目

给定一个长度为 n+1 的数组nums,数组中所有的数均在 1~n 的范围内,其中 n≥1。

请找出数组中任意一个重复的数,但不能修改输入的数组。

样例

给定 nums = [2, 3, 5, 4, 3, 2, 6, 7]。

返回 2 或 3。

思考题:如果只能使用 O(1) 的额外空间,该怎么做呢?

题目来自于这里,有兴趣的可以做一下。

2.时间复杂度为O(nlogn)的解法

自己没想出来,看一位大佬写的题解。

2.1原理

该解法的原理就是抽屉原理与分治。

抽屉原理:n+1个苹果放入到n个抽屉中,那么必定有一个抽屉至少有两个苹果

这道题我们可以理解为,有n+1个位置,我们要给这n+1个位置赋值,范围是1~n,那么必定有一个数要有两个位置。

我们可以对这些数进行分治(不是位置)。在范围1~n当中的数的个数加起来一定是n+1,必定大于n,我们接下去缩小这个范围,当在这个范围中的数的个数加起来大于这个范围的长度,就说明这个范围中一定存在重复的数。依次下去,直到范围的长度缩到1,我们就找到了重复的数了。

2.2代码

int duplicateInArray(vector& nums) {
        int n = nums.size() - 1;
        int left = 1, right = n;
        while(left = left && nums[i]  mid - left + 1)
        		right = mid;
        	else
        		left = mid + 1;
        }
        return right;
    }

3.时间复杂度为O(n)的解法

题解原地址,看完只能说,大佬不愧是大佬,膜拜。

3.1原理

在这里有一点前置的知识,谈一下我看完他们后的看法吧

3.1.1 如何判断一个链存在环

存在环,那么我们就有一个相遇的问题,让一个快的指针和一个慢的指针去循环,它们就会相遇。

所以在这里,我们让快指针每一次走两步,慢指针每一次走一步,如果有环存在,那么两个指针一定会相遇。

3.1.2 快指针与慢指针相遇的地点以及起点的关系

借一下原地址大佬的图
技术图片

首先,快指针速度是慢指针的二倍,相同时间内,快指针的位移一定是慢指针的二倍。

慢指针在进入环后,走完一周之前,一定会与快指针相遇。

将上述两个位移化简得
b + (k - 2l)c = a
公式中k-2L一定是个整数,所以b + 整数倍的圈数 位置依然没有变,我们可以让k = 2L,这样结果是不影响最终位置的。

要细讨论的话是分为三种情况,与a 和 c的大小有关系。(此处就不讨论了)

我们便可以得出一个结论 a = 变化后的b 即相遇的地点与环起点相距b

3.1.3 将知识与题目结合

我们可以将上述知识应用到本题当中。我们可以将数组对应的数当作next值来处理,索引值当作值来处理,由抽屉原理得这个数组一定存在环。

我们先从索引值为0的数开始,将数组对应的值当作next,让快指针移动两格,慢指针移动一格,相遇的地点即P点,接下来我们再让快指针移动到0,然后一格一格的移动直到两个指针相遇,此时索引值就是那个重复的数字。

3.2代码

int duplicateInArray(vector& nums) {
        int low = 0, fast = 0;
        while(low == 0 || low != fast) {
        	low = nums[low];
        	fast = nums[nums[fast]];
        }
        fast = 0;
        while(low != fast) {
        	low = nums[low];
        	fast = nums[fast];
        }
        return fast;
    }

数字中重复的数字(不修改数组且不开辟新空间)

标签:mic   int   判断   抽屉   存在   技术   nlog   不可   接下来   

原文地址:https://www.cnblogs.com/lisuhang/p/14402953.html


评论


亲,登录后才可以留言!