给你一个仅由整数组成的有序数组,其中每个元素都会出现两次,唯有一个数只会出现一次。
请你找出并返回只出现一次的那个数。
你设计的解决方案必须满足 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
提示:
1 <= nums.length <= 105
0 <= nums[i] <= 105
解题思路
- 初始化指针:设定两个指针
left
和right
,分别指向数组的起始和结束位置。- 二分查找:
- 计算中间位置
mid
。- 检查
mid
是否是唯一的数:
- 如果
mid
是偶数且nums[mid] == nums[mid + 1]
,说明唯一的数在右半部分。- 如果
mid
是奇数且nums[mid] == nums[mid - 1]
,也说明唯一的数在右半部分。- 否则,唯一的数在左半部分。
- 调整指针:
- 如果唯一的数在右半部分,将
left
移动到mid + 1
。- 否则,将
right
移动到mid
。- 终止条件:当
left
和right
相遇时,left
指向的就是唯一的数。
代码实现
function singleNonDuplicate(nums) {
let left = 0;
let right = nums.length - 1;
while (left < right) {
let mid = Math.floor((left + right) / 2);
// 检查 mid 是否是偶数
if (mid % 2 === 0) {
// 如果 mid 是偶数且 nums[mid] == nums[mid + 1],说明唯一的数在右半部分
if (nums[mid] === nums[mid + 1]) {
left = mid + 2; // 移动到右半部分
} else {
right = mid; // 移动到左半部分
}
} else {
// 如果 mid 是奇数且 nums[mid] == nums[mid - 1],也说明唯一的数在右半部分
if (nums[mid] === nums[mid - 1]) {
left = mid + 1; // 移动到右半部分
} else {
right = mid; // 移动到左半部分
}
}
}
return nums[left];
}
// 测试用例
console.log(singleNonDuplicate([1, 1, 2, 3, 3, 4, 4, 8, 8])); // 输出: 2
console.log(singleNonDuplicate([3, 3, 7, 7, 10, 11, 11])); // 输出: 10
解释
初始化指针:
left
和right
分别初始化为数组的起始和结束位置。二分查找:
- 计算中间位置
mid
。- 检查
mid
是否是偶数或奇数,并根据相邻元素的关系调整搜索范围。- 如果
mid
是偶数且nums[mid] == nums[mid + 1]
,说明唯一的数在右半部分。- 如果
mid
是奇数且nums[mid] == nums[mid - 1]
,也说明唯一的数在右半部分。- 否则,唯一的数在左半部分。
调整指针:
- 如果唯一的数在右半部分,将
left
移动到mid + 1
或mid + 2
。- 否则,将
right
移动到mid
。终止条件:
- 当
left
和right
相遇时,left
指向的就是唯一的数。