以前作过这样的题,,以前的题是能够修改数组的,那么若是不能够修改数组,咱们如何来作这个题呢?html
给定一个长度为 n+1 的数组nums
,数组中全部的数均在 1∼n 的范围内,其中 n≥1。c++
请找出数组中任意一个重复的数,但不能修改输入的数组。数组
样例指针
给定 nums = [2, 3, 5, 4, 3, 2, 6, 7]。 返回 2 或 3。
思考题:若是只能使用 O(1) 的额外空间,该怎么作呢?code
题目来自于这里,有兴趣的能够作一下。htm
本身没想出来,看一位大佬写的题解。blog
该解法的原理就是抽屉原理与分治。索引
抽屉原理:n+1个苹果放入到n个抽屉中,那么一定有一个抽屉至少有两个苹果ip
这道题咱们能够理解为,有n+1个位置,咱们要给这n+1个位置赋值,范围是1~n,那么一定有一个数要有两个位置。get
咱们能够对这些数进行分治(不是位置)。在范围1~n当中的数的个数加起来必定是n+1,一定大于n,咱们接下去缩小这个范围,当在这个范围中的数的个数加起来大于这个范围的长度,就说明这个范围中必定存在重复的数。依次下去,直到范围的长度缩到1,咱们就找到了重复的数了。
int duplicateInArray(vector<int>& nums) { int n = nums.size() - 1; int left = 1, right = n; while(left < right) { int s = 0; int mid = (left + right) / 2; for(int i = 0; i <= n; i++) { if(nums[i] >= left && nums[i] <= mid) s++; } if(s > mid - left + 1) right = mid; else left = mid + 1; } return right; }
题解原地址,看完只能说,大佬不愧是大佬,膜拜。
在这里有一点前置的知识,谈一下我看完他们后的见解吧
存在环,那么咱们就有一个相遇的问题,让一个快的指针和一个慢的指针去循环,它们就会相遇。
因此在这里,咱们让快指针每一次走两步,慢指针每一次走一步,若是有环存在,那么两个指针必定会相遇。
借一下原地址大佬的图
首先,快指针速度是慢指针的二倍,相同时间内,快指针的位移必定是慢指针的二倍。
慢指针在进入环后,走完一周以前,必定会与快指针相遇。
将上述两个位移化简得
b + (k - 2l)c = a
公式中k-2L必定是个整数,因此b + 整数倍的圈数 位置依然没有变,咱们能够让k = 2L,这样结果是不影响最终位置的。
要细讨论的话是分为三种状况,与a 和 c的大小有关系。(此处就不讨论了)
咱们即可以得出一个结论 a = 变化后的b 即相遇的地点与环起点相距b
咱们能够将上述知识应用到本题当中。咱们能够将数组对应的数看成next值来处理,索引值看成值来处理,由抽屉原理得这个数组必定存在环。
咱们先从索引值为0的数开始,将数组对应的值看成next,让快指针移动两格,慢指针移动一格,相遇的地点即P点,接下来咱们再让快指针移动到0,而后一格一格的移动直到两个指针相遇,此时索引值就是那个重复的数字。
int duplicateInArray(vector<int>& 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; }