通过万岁!!!
- 题目:给你一个数组,数组是有序的,然后每个元素都会出现两次,但是唯独有一个不是。希望能能够找出来。要求时间复杂度O(logn),空间复杂度O(1)。
- 思路:要是没有时间复杂度限制的话,直接就是遍历一遍。然后用一个数加上nums中的奇数,然后减去nums的偶数,得到的结果进行绝对值就行了。时间复杂度是O(n),空间复杂度是O(1)。然后看到这个时间复杂度,而且是一维数组,所以我们想到了二分查找。我们可以发现规律,数组长度一定是奇数,我们二分查找的话,主要是判断向左还是向右。我们可以发现存在几种情况。
1. 中间这个元素跟中间(mid)的左边(mid-1)相同。例如:[1,1,2,3,3,4,4,8,8],[3,3,7,7,10,11,11]。这时候我们发现,我们要的目标有在左边的,也有在右边的,但是可以发现,这两个是有不同的,就是right的下标-mid是奇数和偶数。因此本题中我需要两个条件去判断往左边还是右边。
2. 中间这个元素跟中间(mid)的右边(mid+1)相同。例如:[1,1,2,2,3]。这时候也是会有上面的情况。 - 技巧:
- 使用二分法,注意二分法主要的是需要判断分完以后去左边还是右边
伪代码
首先判断如果数组长度是1的话直接返回nums[0]即可。
然后就是定义l,r,mid三个变量。其中l=0,r=数组的长度-1,mid=(r+l)/2。
然后就是while循环,条件是r!=l
如果mid指向的数与左边和右边都不相等,则直接返回nums[mid],这时候就是找到了
如果mid与mid左边相等,则是上面的第一种情况
然后判断r-mid是奇数还是偶数,如果是奇数
左指针移动
如果是偶数
右指针移动,注意这里移动两个,因为左边第一个与mid是相同的。
否则,就是mid与右边相同,是上面的情况二
然后判断r-mid是奇数还是偶数,如果是奇数
右指针移动
如果是偶数
左指针移动
上面移动了指针,则这时候就需要更新mid了
最后返回nums[r],或者返回nums[l]。
java代码
class Solution {
public int singleNonDuplicate(int[] nums) {
if (nums.length == 1) return nums[0];
// 数组的长度一定是奇数
int l = 0, r = nums.length - 1;
int mid = (l + r) / 2;// 中间元素的下标
while (l != r) {
if (nums[mid] != nums[mid - 1] && nums[mid] != nums[mid + 1]) return nums[mid];
if (nums[mid] == nums[mid - 1]) {
if ((r - mid) % 2 == 1)// 奇数,在右边
l = mid + 1;
else
r = mid - 2;
} else {
if ((r - mid) % 2 == 1)
r = mid - 1;
else
l = mid + 2;
}
mid = (l + r) / 2;
}
return nums[r];
}
}
- 题目不难,主要是有时间复杂度和空间复杂度的限制,但是也正好给我们了一个提示。