给定一个长度为 n 的整数数组 nums
,数组中所有的数字都在 0∼n−1的范围内。
数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。
请找出数组中任意一个重复的数字。
注意:如果某些数字不在 0∼n−1 的范围内,或数组中不包含重复数字,则返回 -1;
最优方法:时间复杂度为O(N),空间复杂度为O(1);
利用数值在0-n-1范围内,有些像鸽洞原理;
class Solution { public: int duplicateInArray(vector<int>& nums) { for(auto x:nums) if(x < 0 || x >=nums.size()) return -1; for(int i = 0;i < nums.size(); i++){ while(nums[i] != i && nums[nums[i]] != nums[i]) //不在正确的位置 && 交换地方也不是正确的位置 swap(nums[i],nums[nums[i]]); if(nums[i]!= i) //因为正常情况下,nums[i]是会在正确的位置 return nums[i]; } return -1; } };
给定一个长度为 n+1的数组nums
,数组中所有的数均在 1∼n 的范围内,其中 n≥1n≥1。
请找出数组中任意一个重复的数,但不能修改输入的数组。
思考题:如果只能使用 O(1)的额外空间,该怎么做呢?
思路:不能修改输入的数组,则不能用上面的方法,也不能排序;
O(1)的方法的话,也不能用hash表;
因此借鉴鸽洞原理(抽屉原理)采用二分查找的方法,每次查找遍历一遍数组,时间复杂度为O(NlogN)
class Solution { public: int duplicateInArray(vector<int>& nums) { int n = nums.size() - 1; int l = 1,r = n; while(l < r){ int m = l + (r-l)/2; int cnt = 0; for(auto x:nums) if(x <= m) cnt++; //nums中<=m的数超过了m个,说明肯定有重复的数在[1-m]中 if(cnt > m) r = m; else l = m+1; } return l; } };