二分变种
540 有序数组中的单一元素
我的二分
- 与右侧配对失败:
- 右侧是奇数:
l = m + 1
- 右侧是偶数:
r = m
- 右侧是奇数:
- 与右侧配对成功:
- 右侧是奇数:
l = m + 2
- 右侧是偶数:
r = m - 1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17class Solution {
public:
int singleNonDuplicate(vector<int>& nums) {
int l = 0, r = nums.size()-1;
while(l < r){ // 这样可以放心取m+1不越界
int m = (l + r) >> 1;
if(nums[m] != nums[m+1]){
if((r-m) & 1) l = m + 1;
else r = m;
}else{
if((r-m-1) & 1) l = m + 2;
else r = m - 1;
}
}
return nums[l];
}
};
- 右侧是奇数:
官方的全数组二分查找
假设要找的是数字X,则在X左边的数,下标是偶数的都是重复数字的第一位,下标是奇数的都是重复数字的第二位;在X右边的数,下标是奇数的都是重复数字的第一位,下标是偶数的都是重复数字的第二位;
取中值时,若m是偶数,则尝试与m+1比较是否相等,相等则表明[:m+1]正常,X在m+1的右侧,因此
l = m + 2
;若不相等,则X在[:m],因此r = m
取中值时,若m是奇数,则尝试与m-1比较是否相等,相等则表明[:m]正常,X在m的右侧,因此
l = m + 1
;若不相等,则X在[:m],因此r = m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17class Solution {
public:
int singleNonDuplicate(vector<int>& nums) {
int l = 0, r = nums.size() - 1;
while (l < r) {
int m = (l + r) >> 1;
if((m & 1) && nums[m] == nums[m-1]){ // 奇数
l = m + 1;
}else if(!(m & 1) && nums[m] == nums[m+1]){ // 偶数
l = m + 2;
}else{
r = m;
}
}
return nums[l];
}
};上述思想可以在代码层面进行简化
m ^ 1
,对于偶数表示m + 1
;对于奇数表示m - 1
,同时照顾到短板,每次更新l
时,有l = m + 1
1 | class Solution { |
官方的偶数范围二分查找
- X的下标一定是偶数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16class Solution {
public:
int singleNonDuplicate(vector<int>& nums) {
int l = 0, r = nums.size()-1;
while (l < r) {
int m = (r - l) / 2 + l;
m -= m & 1; // 变为偶数
if (nums[m] == nums[m + 1]) { // [...m+1]都ok
l = m + 2;
} else {
r = m;
}
}
return nums[l];
}
};