太难了老子不学了
题目
https://leetcode-cn.com/problems/find-the-duplicate-number/
方法一:排序
方法二:哈希表
方法三:二分查找
分析
知道重复的那个数字肯定是 1 到 n(数组长度至少是n+1) 中的某一个,而 1,2…,n 就是一个有序序列。因此我们可以对 1,2…,n 进行二分查找。
我们只需要统计原数组中小于等于 mid+1 的个数,记为 count。
如果 count > mid+1 ,在 [0,mid] 范围内的数字个数超过了 mid ,所以一定有一个重复数字。
否则的话,既然不在 [0,mid] ,那么最终答案一定在 [mid + 1, length-1] 中。
复杂度
时间复杂度:O(nlogn),其中 n 为nums[] 数组的长度。二分查找最多需要二分O(logn) 次,每次判断的时候需要O(n)遍历 nums[] 数组求解小于等于mid 的数的个数,因此总时间复杂度为O(nlogn)。
空间复杂度:O(1)。我们只需要常数空间存放若干变量。
代码
class Solution {
public int findDuplicate(int[] nums) {
int len=nums.length;
if(len==1||len==2)
return nums[0];
int left=1,right=len;
int mid=-1;
while(left<right){
mid=(left+right)/2;
int count=0;
for(int k:nums)
if(k<=mid)
count++;
if(count>mid){
right=mid;
}
else
left=mid+1;
}
return left;
}
}
结果
方法四:快慢指针
其他题参考
【刷题1】LeetCode 141. 环形链表java题解
【刷题1】LeetCode 142. 环形链表 II java题解
分析
我们需要快慢指针,同时从起点出发,慢指针一次走一步,快指针一次走两步,然后记录快慢指针相遇的点。
代码
class Solution {
public int findDuplicate(int[] nums) {
int len=nums.length;
if(len==1||len==2)
return nums[0];
int slow=nums[0];//慢指针走一步
int fast=nums[nums[0]];//快指针走两步
//寻找相遇点
while(slow!=fast){
slow=nums[slow];
fast=nums[nums[fast]];
}
slow=0;
while(slow!=fast){
slow=nums[slow];
fast=nums[fast];
}
return slow;
}
}
复杂度
时间复杂度:O(n)。「Floyd 判圈算法」时间复杂度为线性的时间复杂度。
空间复杂度:O(1)。我们只需要常数空间存放若干变量。
结果
方法五:位运算
复杂度
时间复杂度:O(nlogn),其中 n 为nums[] 数组的长度。O(logn) 代表了我们枚举二进制数的位数个数,枚举第 i 位的时候需要遍历数组统计 x 和 y 的答案,因此总时间复杂度为 O(nlogn)。
空间复杂度:O(1)。我们只需要常数空间存放若干变量。
分析
|:两个位都为0时,结果才为0
代码
class Solution {
public int findDuplicate(int[] nums) {
int res=0;
int n=nums.length;
for(int i=0;i<32;i++){
int a=0;
int b=0;
int mask=(1<<i);
for(int j=0;j<n;j++){
if((nums[j]&mask)>0){
a++;
}
if((j&mask)>0){
b++;
}
}
if(a>b){
res=res|mask;
}
}
return res;
}
}