这道题如果用暴力解法做非常简单,但是题目有要求:Your solution must run in O(log n)
time and O(1)
space.
如果看到时间复杂度O(logn),那么就一定要想到binary search。需要注意的是,在做这类找数题的时候,一定要看清楚是让返回index还是数值啊,博主就犯过这样的错误。
binary search查找的规律,例如:
Input: nums = [3,3,7,7,10,11,11,12,12] Output: 10
1. 如果一个数跟前后都不同,那就是要找的数了。
2. 如果一个数跟前面的数相同,跟后面的数不同,要看这个数的index是奇数还是偶数,如果index是奇数,例如例题中的第二个7,就要往后找,如果index是偶数,例如例题中的第二个11,就要往前找。
3. 如果一个数跟前面的数不同,跟后面的数相同,要看这个数的index是奇数还是偶数,如果index是奇数,例如例题中的第一个11,就要往前找,如果index是偶数,例如例题中的第一个7,就要往后找。
因为既要比较前面的数,又要比较后面的数,数组的第一和最后一个数就要单独判断。所以我的第一个算法是这样的:
public int singleNonDuplicate(int[] nums) { int n = nums.length; if (nums.length <= 2) return nums[0]; if (nums[0] != nums[1]) return nums[0]; if (nums[n - 1] != nums[n - 2]) return nums[n - 1]; int l = 1, r = nums.length - 2, mid = 0; while (l <= r) { mid = (l + r) / 2; if (nums[mid] != nums[mid - 1] && nums[mid] != nums[mid + 1]) return nums[mid]; else if (nums[mid] == nums[mid - 1] && nums[mid] != nums[mid + 1]) { if (mid % 2 == 1) l = mid + 1; else r = mid - 1; } else if (nums[mid] != nums[mid - 1] && nums[mid] == nums[mid + 1]) { if (mid % 2 == 1) r = mid - 1; else l = mid + 1; } } return -1; }
我们能不能只跟前面或者后面比较呢,还是上面的例子, 我们试试只跟前面比较:
Input: nums = [3,3,7,7,10,11,11,12,12]
index: [0,1,2,3,4, 5, 6, 7, 8] Output: 10
1. 如果一个数跟前面的数相同,要看这个数的index是奇数还是偶数,如果index是奇数,例如例题中的第二个7,就要往后找,如果index是偶数,例如例题中的第二个11,就要往前找。
2. 如果一个数跟前面的数不同,要看这个数的index是奇数还是偶数,如果index是奇数,例如例题中的第一个11,就要往前找,如果index是偶数,例如例题中的第一个7,就要往后找。
3. 目标的index一定是偶数。
public int singleNonDuplicate2(int[] nums) { int l = 0, r = nums.length - 1, mid = 0; while (l < r) { mid = (l + r) / 2; if (nums[mid] == nums[mid - 1]) { if (mid % 2 == 1) l = mid + 1; else r = mid; } else if (nums[mid] != nums[mid - 1]) { if (mid % 2 == 1) { r = mid - 1; } else l = mid; } } return nums[l]; }