给你一个仅由整数组成的有序数组,其中每个元素都会出现两次,唯有一个数只会出现一次。请你找出并返回只出现一次的那个数。
你设计的解决方案必须满足 O(log n)
时间复杂度和 O(1)
空间复杂度。
示例 1:
输入: nums = [1,1,2,3,3,4,4,8,8] 输出: 2
示例 2:
输入: nums = [3,3,7,7,10,11,11] 输出: 10
思路
本来是O(n)循环遍历一圈就可以,但是题目要求时间复杂度必须是 O(log n)
,所以要采用二分法,同时空间复杂度要满足 O(1),所以我们要采用一种不通过比较的方法找到只出现一次的数字。
通过观察和理解题意,因为其他数字都是成对出现,并且只有我们要找的数字出现一次,因此 nums 的总长度一定是奇数,因此我们一定能找到唯一的 mid 值。同时我们只要比较 mid 和它左边或右边的数字是否相等,就能找到出现一次的数字在 mid 左边还是右边。
因为如果 mid 是奇数,则应该和它右边的数字相比较,因为成对出现的数字一定会形成偶数长度。如果 mid 是偶数则和它左边的数字相比较。然后更新端点即可。
代码(C++)
class Solution {
public:int singleNonDuplicate(vector<int>& nums) {int low = 0, high = nums.size() - 1;while (low < high) {int mid = (high - low) / 2 + low;if (nums[mid] == nums[mid ^ 1]) {low = mid + 1;} else {high = mid;}}return nums[low];}
};
1. 异或操作符 (^
)
异或操作符 (^
) 是一种位操作符,用于对两个二进制数的每一位进行比较。如果两个位相同,结果为 0
,如果两个位不同,结果为 1
。例如:
-
0 ^ 0 = 0
-
0 ^ 1 = 1
-
1 ^ 0 = 1
-
1 ^ 1 = 0
在整数上使用异或操作符时,结果是两个整数的每一位进行异或操作后的结果。例如:
-
5 ^ 1 = 4
(二进制表示:101 ^ 001 = 100
) -
6 ^ 1 = 7
(二进制表示:110 ^ 001 = 111
)
2. mid ^ 1
的作用
在二分查找或其他算法中,mid
通常表示当前的中间索引。mid ^ 1
的作用是根据 mid
的奇偶性来选择相邻的索引:
-
如果
mid
是偶数,mid ^ 1
会得到mid + 1
。 -
如果
mid
是奇数,mid ^ 1
会得到mid - 1
。
这是因为:
-
对于偶数
mid
,mid
的二进制表示的最后一位是0
,所以mid ^ 1
会将最后一位从0
变为1
,即mid + 1
。 -
对于奇数
mid
,mid
的二进制表示的最后一位是1
,所以mid ^ 1
会将最后一位从1
变为0
,即mid - 1
。