难度中等288收藏分享切换为英文接收动态反馈
给定一个只包含整数的有序数组,每个元素都会出现两次,唯有一个数只会出现一次,找出这个数。
示例 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
进阶: 采用的方案可以在 O(log n)
时间复杂度和 O(1)
空间复杂度中运行吗?
通过次数41,206提交次数70,848
题目分析
方法一:
新手容易想到的方法是遍历数组,用哈希表记录出现数字出现频数,然后找到唯一的返回即可。
方法二:
另外一种做法是,用unordered_set去重得到数组nums2,分别对nums1和nums2求和,nums2*2-nums1即为答案。
方法三:
还有一种做法是利用异或运算的性质。0异或任意一个数都是这个数,任意一个数异或自己都是0,再利用到异或运算的可结合性,可以发现定义变量res = 0,然后遍历数组nums1,分别求异或即是正确答案。
方法四:
可以利用指针遍历。维护指针,使其始
终位于连续子数组a,a是每个元素都会出现两次的数组,的下一个元素。然后判断指针所在元素的下一个元素是否与其相等即可。
方法五:
利用二分查找。首先肯定要中分嘛。问题是,中分之后,该如何取舍区间呢?答案是,看左右两侧的区间是奇数个还是偶数个。
做关于数组的题目的时候我很喜欢思考,这个数组的性质是什么样子的,往往一些不起眼的小性质就是破题之道,比如这道题其实就隐含了一个条件——给定数组的长度一定为奇数。那么,中间位置元素若不是目标元素(分别-1、+1判断),那么答案所在的区间应该是以中间未知元素对应的那个数对(有两个连在一起的相同数字)为界的左右两个区间的长度为奇数位的那一个。
class Solution {//异或运算
public:
int singleNonDuplicate(vector<int>& nums) {
int res = 0;
for(int i : nums){
res ^= i;
}
return res;
}
};
class Solution {//双指针
public:
int singleNonDuplicate(vector<int>& nums) {
int pi = 0;//维护pi使其一直在已维护好的数组前端哈哈哈
int n = nums.size();
while(pi<n){
if(pi+1<n&&nums[pi+1]!=nums[pi]){
return nums[pi];
}
else{
pi+=2;
}
}
return nums[n-1];
}
};
class Solution {//二分查找
public:
int singleNonDuplicate(vector<int>& nums) {
int n = nums.size();
int left = 0;
int right = n - 1;
int mid;
while(left<right){
mid = left + right >> 1;
if(nums[mid] == nums[mid-1]){//边界判断问题?
if((mid - left - 1)%2){
right = mid - 2;
}
else{
left = mid+1;
}
}
else if(nums[mid] == nums[mid+1]){
if((mid-left)%2){
right = mid-1;
}
else{
left = mid+2;
}
}
else{
return nums[mid];
}
}
return nums[right];
}