「这是我参与2022首次更文挑战的第26天,活动详情查看:2022首次更文挑战」。
每日刷题第48天 2021.02.16
540. 有序数组中的单一元素
- leetcode原题链接:leetcode-cn.com/problems/si…
- 难度:中等
- 方法:二分查找
题目
- 给你一个仅由整数组成的有序数组,其中每个元素都会出现两次,唯有一个数只会出现一次。
- 请你找出并返回只出现一次的那个数。
- 你设计的解决方案必须满足 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 <= 1050 <= nums[i] <= 105
解法
二分算法binarySearch:折半查找
- 整体思路(三步走)
- 预处理数据:将无序数组排列成有序的数组
- 二分查找:使用
while循环或者递归的方式对数组进行折半查找 - 后处理:在剩余的空间中查找可行的候选者(后处理这一步也可以直接写在每次二分查找的过程中)
需要注意的点⚠️
- 溢出问题:取
low和high的中间值的时候,low + high可能会溢出(溢出:超过当前数据类型的最大值),可以将其修改为low + (high - low) / 2 - 边界问题:在每次给
low与high赋值的时候,需要想清楚边界范围。- 前闭后开
[low,high),while循环条件low < high - 前闭后闭
[low,high],while循环条件low <= high,因为[low.high]还存在一个数组未查找。
- 前闭后开
解题思路
- 当有序数组中不存在不重复的数时,全部都符合偶数下标为数出现第一次,奇数下标为数出现第二次。
- 举例:
[4,4,6,6,8,8]下标:0和1、2和3、4和5
- 举例:
- 当有序数组中存在不重复的数后,上述规律被打破。出现不重复数的前半段还保留上述的规则,而不重复数的后半段变成了:奇数下标为数第一次出现,偶数下标为数第二次出现。
- 举例:
[4,4,6,6,3,8,8]下标:0和1、2和3、4、5和6
- 举例:
- 那么根据上述推导出来的规律,就可以进行判断,当前需要查找的不重复的数的区间。设需要查找的不重复的数为
x- 当
mid为偶数时,判断nums[mid] == nums[mid + 1] - 当
mid为奇数时,判断nums[mid] == nums[mid - 1] - 若相等,则表示
x > nums[mid](因为前半段规律并没有被破坏);否则,x < nums[mid]。
- 当
小技巧(使用异或^)
- 异或:当两数相等时,异或为
0;反之,为1- 举例:
0 ^ 0 = 0 、1 ^ 1 = 0、0 ^ 1 = 1、1 ^ 0 = 1
- 举例:
/**
* @param {number[]} nums
* @return {number}
*/
var singleNonDuplicate = function(nums) {
// 二分查找
let low = 0;
let mid = 0;
let high = nums.length - 1;
while(low < high) {
// ^ 1,偶数+1,奇数-1
// 不相同,为1;相同为0
mid = parseInt((high - low) / 2) + low;
if(nums[mid] == nums[mid ^ 1]){
// x > mid
low = mid + 1;
}else {
// x < mid
high = mid;
}
}
return nums[low];
};