一、题目描述
给定一个只包含整数的有序数组nums
,每个元素都会出现两次,唯有一个数只会出现一次,请找出这个唯一的数字。
你设计的解决方案必须满足 O(log n)
时间复杂度和 O(1)
空间复杂度。
示例 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
二、思路讲解
2.1 哈希表
找个数的题目,用HashMap可以通杀。使用哈希表保存每个数字出现的次数,然后找到值为1的即可。
2.2 一次循环
因为数组本身是有序的(即使的无序的也可以给他排序),所以相等的数字必然相邻,那么我们可以一遍循环,找到一个数字,他的左边和右边跟自己都不相等,这个数字就是只出现一次的数字。
时间复杂度: O(N)
空间复杂度: O(1)
2.3 二分查找
然而,题目中要求的时间复杂度为对数级。由经验可得,有序数组的查找基本上可以使用二分查找,因此我们取左指针low指向最左,右指针high指向最右,中点mid指向中间,可以注意到:
1、当mid为偶数时:
——如果nums[mid]跟左边的数字相等,那么所求数字一定出现在mid的左边,可以直接令high = mid -1。例如:
1 1 2 3 3 4 4 5 5
low mid high
——如果nums[mid]跟右边数字相等,那么所求数字一定出现在mid右边。例如:
1 1 2 2 3 3 4 5 5
2、当mid为奇数时:
——如果nums[mid]跟左边数字相等,那么所求数字定出现在mid右边。例如:
1 1 2 2 3 3 4 4 5 6 6
low mid high
——如果nums[mid]跟右边数字相等,则相反。例如:
1 1 2 3 3 4 4 5 5 6 6
这样我们就已经确定了二分查找的大致思路。
剩下的困难就是边界的判断问题了,我们一起看看代码里是怎么做的:
class Solution { public int singleNonDuplicate(int[] nums) { if(nums.length==1 || nums[0]!=nums[1]) { return nums[0]; } int low = 0; int high = nums.length - 1; while(low <= high) { //这样写在任何情况下都不会溢出 int mid = (high - low) / 2 + low; //如果mid达到了边界,就给left或right一个不可能的值,这样可以简化边界的判断 int left = (mid>0)? nums[mid-1] : -1; int right = (mid<nums.length-1)? nums[mid+1] : Integer.MAX_VALUE; //如果nums[mid]跟两边都不相等,说明我们找的就是他 if(nums[mid]!=left && nums[mid]!=right) { return nums[mid]; } if(mid%2 == 0) { if(nums[mid] == right) { low = mid + 1; } else{ high = mid -1; } } else { if(nums[mid] == left) { low = mid + 1; } else { high = mid - 1; } } } return nums[low]; } }
时间复杂度: O(logN)
空间复杂度: O(1)
2.4 位运算
利用按位异或的性质,可以减少对mid判断奇偶性的步骤,减少代码量,算得上是奇技淫巧了:
当mid为偶数时,mid + 1 = mid ^ 1;
当mid为奇数时,mid - 1 = mid ^ 1;
class Solution { public int singleNonDuplicate(int[] nums) { int low = 0, high = nums.length - 1; while (low < high) { int mid = (high - low) / 2 + low; if (nums[mid] == nums[mid ^ 1]) { low = mid + 1; } else { high = mid; } } return nums[low]; } } 作者:力扣官方题解 链接:https://leetcode.cn/problems/skFtm2/solutions/1252765/pai-xu-shu-zu-zhong-zhi-chu-xian-yi-ci-d-jk8w/ 来源:力扣(LeetCode) 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。