难度:中等
目录
一、问题描述
这里直接采用LeetCode上面的描述。
给你一个仅由整数组成的有序数组,其中每个元素都会出现两次,唯有一个数只会出现一次。请你找出并返回只出现一次的那个数。
你设计的解决方案必须满足 时间复杂度和 空间复杂度。
下面给出示例:
提示:
1 <= nums.length <= 105
0 <= nums[i] <= 105
二、思路
1、解题思路
1.思路一
这里题目中给出的要求有:
- 时间复杂度
- 空间复杂度
很明显是需要使用 二分查找 来解题。
题目中说明了 有序数组 且 每个元素都会出现两次,唯有一个数只会出现一次。那么查看示例我们可以发现:设当前下标为 x
- 在出现 唯一数 之前,当 x 为偶数时,则肯定是 满足 nums[ x ] = nums[ x + 1 ],若 x 是奇数,则肯定满足 nums[ x ] = nums[ x - 1 ]。
- 出现了 唯一数 之后,当 x 为偶数时,则肯定是 满足 nums[ x ] = nums[ x - 1 ],若 x 是奇数,则肯定满足 nums[ x ] = nums[ x + 1 ]。
因此我们可以根据上述条件来进行 二分条件 的判断,因为 只会出现 一个 唯一数,所以数组 nums 的大小肯定是 奇数 。
我们假设当前下标为 x :
- 如果 x 是奇数,则与 x 左边的数进行相等判断,不相等 则说明唯一数在 mid 的左边,相等 则说明 唯一数 在 x 的右边。
- 如果 x 是偶数,则与 x 右边的数进行相等判断,不相等 则说明唯一数在 mid 的左边,相等 则说明唯一数在 x 的右边。
根据上面条件,可以写出二分查找 ,这里采用的是 异或( ^ ) 来区分当前 mid 的 奇偶性,确实特别巧妙。
2.思路二
我想到的是,每次进行跳跃两个元素的查找,直至查找到 nums[ x ] != nums[ x + 1 ] 的时候,那么 nums[ x ],肯定是那个 唯一元素。
2、极端情况判断
- 对于方法二:这里需要注意的是,判断条件 不能让下标 x + 1 越界,那么就判断不到最后一个元素,并且判断不到 nums大小 为 1 的情况。
3、极端情况解决
- 可以发现如果判断到最后,都没发现唯一元素的话,那么唯一元素只能是最后一个;如果只有一个元素的话,那么唯一元素也是最后一个元素。所以解决办法就是,最后直接 return nums[ nums.size() - 1 ] ,即可。
三、解题
1、代码实现
1.方法一
class Solution {
public:
int singleNonDuplicate(vector<int>& nums) {
int left = 0, right = nums.size() - 1;
while (left < right) {
//每次都找到 left 与 right 中间的数 进行判断
int mid = (right - left) / 2 + left;
//这里采用与 1 异或 简直很完美
//因为 如果 mid 是奇数 则同左边数进行比较,如果 mid 是偶数 则同右边数进行比较
//这里的二分条件为:俩数相等 那么肯定在 mid 右边出现唯一的数
if (nums[mid] == nums[mid ^ 1]) {
left = mid + 1;
}
//如果不相等 则说明 顺序肯定是在左边被打乱的。
else {
right = mid;
}
}
return nums[left];
}
};
2.方法二
class Solution {
public:
int singleNonDuplicate(vector<int>& nums) {
int numsLength = nums.size();
for(int i = 0; i < numsLength - 1; i += 2){
if(nums[i] == nums[i+1]){
continue;
}else{
return nums[i];
}
}
return nums[numsLength - 1];
}
};
2、时间复杂度 and 空间复杂度
1.方法一
时间复杂度:
空间复杂度:
2.方法二
时间复杂度:
空间复杂度: