题目描述
题目思路
本题的关键在于意识到在单独的元素出现以前,由于数组是有序的,两两相同的元素都排在一起,观察下标可以发现:
1 1 2 2 3 4 4
0 1 2 3 4 5 6
n
u
m
s
[
y
]
=
=
n
u
m
s
[
y
+
1
]
⇔
y
是
偶
数
n
u
m
s
[
y
−
1
]
=
=
n
u
m
s
[
y
]
⇔
y
是
奇
数
nums[y] == nums[y + 1] \Leftrightarrow y是偶数\\ nums[y - 1] == nums[y] \Leftrightarrow y是奇数\\
nums[y]==nums[y+1]⇔y是偶数nums[y−1]==nums[y]⇔y是奇数
当单独的元素出现以后,有:
n
u
m
s
[
z
]
=
=
n
u
m
s
[
z
+
1
]
⇔
z
是
奇
数
n
u
m
s
[
z
−
1
]
=
=
n
u
m
s
[
z
]
⇔
z
是
偶
数
nums[z] == nums[z + 1] \Leftrightarrow z是奇数\\ nums[z - 1] == nums[z] \Leftrightarrow z是偶数\\
nums[z]==nums[z+1]⇔z是奇数nums[z−1]==nums[z]⇔z是偶数
所以我们可以利用这个性质进行二分,找满足第一个性质区间的右端点或者找满足第二个性质区间的左端点。
class Solution {
public:
int singleNonDuplicate(vector<int>& nums)
{
int n = nums.size();
int l = 0, r = n - 1;
while (l < r)
{
/*因为更新时是l = mid 所以需要加1*/
int mid = (l + r + 1) >> 1;
if ((mid & 1) == 0)
{
if (mid > 0 && nums[mid - 1] == nums[mid])
r = mid - 1;
else l = mid;
}
else
{
if (mid + 1 < n && nums[mid] == nums[mid + 1])
r = mid - 1;
else l = mid;
}
}
return nums[l];
}
};
本题还可以进一步优化,思考一个位运算异或,若x是奇数,则x ^ 1 = x - 1
,若x是偶数,则x ^ 1 = x
,利用异或可以把两种情况归结到一起:
class Solution {
public:
int singleNonDuplicate(vector<int>& nums)
{
//本题的关键在于在单个元素x出现以前
//若nums[y] == nums[y + 1] 则y必为偶数
//在单个元素x出现以后
//若nums[z] == nums[z + 1] 则z必为奇数
//可以利用这个性质二分
//更进一步的 当mid是奇数时 mid ^ 1 = mid - 1
//当mid是偶数时 mid ^ 1 = mid + 1
//我们找满足nums[y] == nums[y + 1] <-> y为偶数
//nums[y - 1] == nums[y] <-> y是奇数
//的右边界
//所以我们只要比较nums[mid] == nums[mid ^ 1]是否成立即可 不必分奇偶讨论
int l = 0, r = nums.size() - 1;
while (l < r)
{
int mid = (l + r) >> 1;
/*若满足这个性质 则说明mid还在左区间内 为了找边界 让l = mid + 1*/
/*这里不会越界 因为若mid等于0 异或1还是0
且mid = (l + r) / 2 <= r mid + 1 大于r的时候不会大于n - 1
*/
if (nums[mid] == nums[mid ^ 1]) l = mid + 1;
/*否则说明mid不在左区间内 更新r为mid*/
else r = mid;
}
return nums[l];
}
};
观察下标还能观察到:
1 1 2 2 3 4 4
0 1 2 3 4 5 6
因为单独出现的数字的左边都是成对的相同出现的数,所以单独出现的数的下标一定是偶数,可以对偶数下标进行二分,保持mid是偶数的条件是mid -= mid & 1
,mid是奇数时,mid & 1
是1;mid
是偶数时,mid & 1
是0.
第一个不满足性质nums[y] == nums[y + 1]
的偶数下标即为答案。
class Solution {
public:
int singleNonDuplicate(vector<int>& nums)
{
int n = nums.size();
int l = 0, r = n - 1;
while (l < r)
{
int mid = (l + r) >> 1;
mid -= (mid & 1);
/*若满足条件 则让l往前走两步*/
if (mid + 1 < n && nums[mid] == nums[mid + 1])
l = mid + 2;
/*若不满足 让r = mid 控制一定能扫到答案*/
else r = mid;
}
return nums[r];
}
};