acwing链接
这里题目要求不修改原来的数组,并且空间复杂度要求为
O
(
1
)
O(1)
O(1)。这里可以用二分查找的方法。设
l
e
f
t
left
left是搜索范围的开始,
r
i
g
h
t
right
right为搜索范围的结束。
m
i
d
mid
mid为它们的中间值
统计数组中
[
l
e
f
t
,
m
i
d
]
[left, mid]
[left,mid]之间数字的次数
c
n
t
cnt
cnt,与left和right之间的数字个数
m
i
d
−
l
e
f
t
+
1
mid- left + 1
mid−left+1进行比较。
- l e f t = r i g h t left = right left=right并且出现频率大于1,则返回 l e f t left left,否则返回-1
- 如果 c n t > m i d − l e f t + 1 cnt > mid- left + 1 cnt>mid−left+1。说明重复数字在 [ l e f t , m i d ] [left, mid] [left,mid]之间,将 r i g h t right right修改为 m i d mid mid
- 否则,说明重复数字不在 [ l e f t , m i d ] [left, mid] [left,mid],将 l e f t left left修改为 m i d + 1 mid + 1 mid+1。注意是修改为 m i d + 1 mid + 1 mid+1,不然会出现卡死的情况。
- 进行下一次搜索
/*
利用二分查找,不断地缩小重复数字的范围
算法复杂度O(nlogn)
*/
class Solution {
public int duplicateInArray(int[] nums) {
int left = 1;
int right = nums.length - 1;
while(left < right){
int mid = (left + right) / 2;
int cnt = cntInRange(nums, left, mid);
if(left == right){
if(cnt > 1)
return left;
else
break;
}
if(cnt > mid - left + 1)
right = mid;
else
left = mid + 1;
// System.out.println("left = " + left + " right = " + right);
}
return -1;
}
// 用于统计nums中在[start, end]数字个数
private int cntInRange(int[] nums, int start, int end){
int cnt = 0;
for(int num : nums){
if(num >= start && num <= end)
cnt++;
}
return cnt;
}
}
一个小问题,假如镜像地操作,统计 [ m i d , r i g h t ] [mid, right] [mid,right]之间的数字而不是 [ l e f t , m i d ] [left, mid] [left,mid]的数字,那么就会出现卡死。知道卡死的原因。例如统计[3,4,4],前者会出问题,后者不会。
/*
利用二分查找,不断地缩小重复数字的范围
算法复杂度O(nlogn)
*/
class Solution {
public int duplicateInArray(int[] nums) {
int left = 1;
int right = nums.length - 1;
while(left < right){
int mid = (left + right) / 2;
int cnt = cntInRange(nums, mid, right);
if(cnt > right - mid + 1)
left = mid;
else{
right = mid - 1;
}
// 二分查找法很容易卡死,这里假如选择的是统计[mid, right]来判断,就会卡死。莫名其妙
}
return left;
}
// 用于统计nums中在[start, end]数字个数
private int cntInRange(int[] nums, int start, int end){
int cnt = 0;
for(int num : nums){
if(num >= start && num <= end)
cnt++;
}
return cnt;
}
}