【剑指offer刷题记录 java版】1、数组中重复的数字
题目一:找出数组中重复的数字
关键:这种数组元素在 [0, n-1] 范围内的问题,可以将值为 i 的元素放到第 i 个位置上。
一、哈希表记录法
时间复杂度为O(n),空间复杂度为O(n)
代码如下:
public int findRepeatNumber(int[] nums) {
//如果数组不可能有重复数字,则返回-1。
//(本题不会出现,因为限制长度2 <= n <= 100000
if(nums.length<2){
return -1;
}
int[] count = new int[nums.length];
for(int i = 0; i<nums.length; i++){
if(++count[nums[i]] >1){
return nums[i];
}
}
return -1;
}
二、最优思路
时间复杂度为O(n),空间复杂度为O(1)
实现过程:
从头到尾依次扫描这个数字中的每个数字,当扫描到下标为i的数字,首先比较这个数字(用m表示)是不是等于i,如果是,则继续扫描下一个,如果不是,就就拿它和第m个数字比较,如果它和第m个数字相等,就找到了一个重复的数字;如果不相等,就把第i个数字与第m个数字交换,把m放到属于它的位置,接下来再重复这个比较,交换的过程,直到我们发现一个重复的数字。
代码如下:
public int findRepeatNumber(int[] nums) {
//如果数组不可能有重复数字,则返回-1。
//(本题不会出现,因为限制长度2 <= n <= 100000
if(nums.length<2){
return -1;
}
for(int i = 0; i<nums.length; i++){
//比较第i个数是否等于i
// 等于i则扫描下一个(不进入循环)
// 不等于i(进入循环)
while(i!=nums[i]){
int m = nums[i];
//比较第m个数是否等于m,等于则找到重复数返回
if(nums[i]==nums[m]){
return nums[i];
}
//不等于m则交换第i和第m个数
//交换后继续对第i的数重复上述操作,直至找到重复数
nums[i] = nums[m];
nums[m] = m;
}
}
//如果遍历数组都没找到重复数,返回-1
return -1;
}
题目二:不修改数组找出数组中重复的数字
在一个长度为n+1的数组里的所有数字都在1~n的范围内,所以数组中至少有一个数字是重复的。请找出数组中任意一个重复的数字,但是不能修改输入的数组。例如,如果输入长度为8的数组{2,3,5,4,3,2,6,7},那么对应的输出是重复的数字2或者3。
一、哈希表记录法
时间复杂度为O(n),空间复杂度为O(n)
代码如下:
public int findRepeatNumber(int[] nums) {
int[] count = new int[nums.length];
for(int i = 0; i<nums.length; i++){
if(++count[nums[i]] >1){
return nums[i];
}
}
return -1;
}
二、仿二分查找(时间换空间
时间复杂度为O(nlog(n)),空间复杂度为O(1)
代码如下:
public int findDuplicate(int[] nums) {
int left = 1;
int right = nums.length;
while(left<right){
int middle = (left+right)>>1; //除以二可以用位运算>>1代替
//遍历统计小于等于middle的个数
int count = 0;
for(int i=0; i<nums.length; i++){
if(nums[i]>=left && nums[i]<=middle){
count++;
}
}
//如果个数大于左区间长度 重复元素在左区间 缩小区域
if(count > middle-left+1){ //[1,mid] [mid+1,r]
right = middle;
}else {
left = middle + 1;
}
}
// 退出while循环 left = right
return left;
}