标签:mic int 判断 抽屉 存在 技术 nlog 不可 接下来
之前做过这样的题,,之前的题是可以修改数组的,那么如果不可以修改数组,我们如何来做这个题呢?
给定一个长度为 n+1 的数组nums
,数组中所有的数均在 1~n 的范围内,其中 n≥1。
请找出数组中任意一个重复的数,但不能修改输入的数组。
样例
给定 nums = [2, 3, 5, 4, 3, 2, 6, 7]。
返回 2 或 3。
思考题:如果只能使用 O(1) 的额外空间,该怎么做呢?
题目来自于这里,有兴趣的可以做一下。
自己没想出来,看一位大佬写的题解。
该解法的原理就是抽屉原理与分治。
抽屉原理:n+1个苹果放入到n个抽屉中,那么必定有一个抽屉至少有两个苹果
这道题我们可以理解为,有n+1个位置,我们要给这n+1个位置赋值,范围是1~n,那么必定有一个数要有两个位置。
我们可以对这些数进行分治(不是位置)。在范围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;
}
标签:mic int 判断 抽屉 存在 技术 nlog 不可 接下来
原文地址:https://www.cnblogs.com/lisuhang/p/14402953.html