不修改数组找出重复的数字
题目:
给定一个长度为 n+1的数组nums
,数组中所有的数均在 1∼n的范围内,其中 n≥1。
请找出数组中任意一个重复的数,但不能修改输入的数组。
样例
给定 nums = [2, 3, 5, 4, 3, 2, 6, 7]。
返回 2 或 3。
思考题:如果只能使用 O(1)O(1) 的额外空间,该怎么做呢?
题解:
这个题跟上一个找出重复数组元素的值很像,当然也可以采用上述题的方法,但是,上述方法用到了o(n)的空间,这里的思考要求使用o(1),那么,可以有以下两种解法:
-
前后指针
这种解法有一个前提条件,就是数组中一定会存在重复元素,而题中也符合此条件。
规定一个快指针fast初值为0,一个满指针slow初值为0,快指针先去找数组元素,利用fast=nums[fast];fast = nums[fast];不停的跳,这里有两句,意在比slow快,fast在数组不断的跳,因为会有的重复的,所以fast会出现在一个值或者多个值重复的情况,此时slow = nums[slow];慢指针在后面紧紧跟随,由于fast陷入重复,那么slow就会追上fast,此刻结束循环,找出重复值即可。
class Solution { public: int duplicateInArray(vector<int>& nums) { int fast = 0; int slow = 0; while(1){ fast=nums[fast]; fast = nums[fast]; slow = nums[slow]; if(fast==slow) break; } slow = 0; while(1){ fast = nums[fast]; slow=nums[slow]; if(fast==slow)return slow; } } };
-
二分法
这里的二分法与其他的二分法,有些不同,分的不是原来的数组,而是范围,假设范围是17,那么分为两份就是13和47,然后从数组中找到符合两个段的值,以此题为例,符合13的是4个,符合47的也是4个,但是,13只有1,2,3三个元素,此刻却有四个,那么这个范围内的必然有重复的,因此继续范围,直到找到那个重复值。
class Solution { public int duplicateInArray(int[] nums) { int len = nums.length; if (len == 0) {return -1;} int lo = 1; int hi = len - 1; while(lo <= hi) { int mid = (hi + lo) / 2; int count = 0; for (int n:nums) { if (n >= lo && n <= mid) { count++; } } if (hi == lo) { if (count > 1) { return hi; } else { break; } } if (count > mid - lo + 1) { hi = mid; } else { lo = mid + 1; } } return -1; } }