【LeetCode-数组】有序数组中的单一元素
题目描述
给定一个只包含整数的有序数组,每个元素都会出现两次,唯有一个数只会出现一次,找出这个数。
示例:
输入: [1,1,2,3,3,4,4,8,8]
输出: 2
输入: [3,3,7,7,10,11,11]
输出: 10
注意:
您的方案应该在 O(log n)时间复杂度和 O(1)空间复杂度中运行。
题目链接: https://leetcode-cn.com/problems/single-element-in-a-sorted-array/
思路1
将数组中的值进行异或,最终的结果就是只出现一次的数字。
class Solution {
public:
int singleNonDuplicate(vector<int>& nums) {
int ans = nums[0];
for(int i=1; i<nums.size(); i++){
ans ^= nums[i];
}
return ans;
}
};
这种思路没有用到数组有序这个条件,而且时间复杂度为 O(n),不是题目要求的 O(log n)。
- 时间复杂度:O(n)
- 空间复杂度:O(1)
思路2
题目要求时间复杂度为 O(log n),这就在暗示我们使用二分查找来做。二分查找的关键点就是如何更新 left 和 right。假设当前查找的范围为 [left, right],mid = (left+right)/2。如果 mid 为偶数,则说明 mid 前面有偶数个数,例如 mid = 2,则 mid 前面有 2 个数(下标为 0, 1);如果 mid 为奇数,则说明 mid 前面有奇数个数。
如果 mid 为偶数,则 mid 前面有偶数个数,我们判断 nums[mid] 和 nums[mid+1] 是否相等。
- 如果两者相等,则说明前面偶数个数字都是出现两次的数字。例如,数组为 [1,1,2,2,3],mid == 2 为偶数,nums[mid]==nums[mid+1]==2,所以 mid 前面的偶数个数字都出现了两次(两个 1),则出现一次的数字在 mid 右边,此时我们更新 left = mid + 1;
- 否则,如果 nums[mid] 和 nums[mid+1] 不相等,则出现一次的数字在 mid 左边,此时更新 right = mid - 1。例如,数组为 [1,2,2,3,3],mid == 2,nums[mid]!=nums[mid+1], 1 在 mid 左边。
如果 mid 为奇数,则 mid 前面有奇数个数,我们同样判断 nums[mid] 和 nums[mid+1] 是否相等。
- 如果两者相等,则说明只出现一次的数在 mid 左边,更新 right = mid - 1。例如,数组为 [1,2,2,3,3,4,4],mid == 3 为奇数,nums[mid]==nums[mid+1],所以 1 在 mid 左边。
- 否则,如果两者不等,则说明只出现一次的数在 mid 右边,更新 left = mid + 1。例如,数组为 [1,1,2,2,3,3,4],mid==3,nums[mid]!=nums[mid+1],所以 4 在 mid 右边。
总结一下:
- 首先判断 mid 的奇偶性,通过 mid 的奇偶性可以知道 mid 前有奇数个元素还是偶数个元素,然后通过 nums[mid] 和 nums[mid+1] 是否相等来缩小查找范围。
- 如果 mid 为偶数:
- 判断 nums[mid] 和 nums[mid+1] 是否相等:
- 相等,则更新 left = mid + 1;
- 不等,则更新 right = mid - 1;
- 判断 nums[mid] 和 nums[mid+1] 是否相等:
- 如果 mid 为奇数:
- 判断 nums[mid] 和 nums[mid+1] 是否相等:
- 相等,则更新 right = mid - 1;
- 不等,则更新 left = mid + 1;
- 判断 nums[mid] 和 nums[mid+1] 是否相等:
代码如下:
class Solution {
public:
int singleNonDuplicate(vector<int>& nums) {
int left = 0;
int right = nums.size()-1;
while(left<=right){
int mid = left+(right-left)/2;
if(mid%2==0 && mid+1<nums.size()){ // mid 为偶数
if(nums[mid]==nums[mid+1]) left = mid+1; // nums[mid]==nums[mid+1]
else right = mid-1;
}else if(mid%2!=0 && mid+1<nums.size()){ // mid 为奇数
if(nums[mid]==nums[mid+1]) right = mid-1; // nums[mid]==nums[mid+1]
else left = mid+1;
}else return nums[mid]; // 注意这一步说明 mid 为最后一个元素
}
return nums[left]; // 最后返回 nums[left]
}
};
- 时间复杂度:O(logn)
- 空间复杂度:O(1)