这是我参与2022首次更文挑战的第19天,活动详情查看:2022首次更文挑战
题目
540. 有序数组中的单一元素
2022-02-14的每日一题
解析
这个题目,之前也做过类似的,条件是:
一个随机数组(未排序),有且仅有一个数在其中出现一次,其余的都出现两次。
求取:这个仅出现一次的数。
那个题目的核心思路就是:
- 利用位运算中的异或操作,将相同的数两两消除。
那么到最后的结果,剩下的就是仅出现一次的那个数了。
这个题目条件就不同:
随机数组->排序数组
算法要求:O(logn)的时间复杂度,O(1)的空间复杂度。
根据这个算法要求,容易知道常见的算法中,二分恰好满足这个要求。
其实如果考虑到递归栈空间的空间使用,这里就必须使用迭代而非递归。
条件分析
这里的排序,实际上对于解题的帮助是:
- 由于数都排序好了,那么相同的数必然是相邻的。
还有一个隐含的条件:
- 由于只有一个数是重复的,那么包含单独的数的那一部分,长度的值必然是奇数。
根据这个条件,就可以进行二分的判断了。
代码结构整理
在二分的判断中,极其重要的就是二分的跳转条件。
这里需要注意的有两点:
-
i到j中,包含的数共j-i+1个。
-
对于已经检查的数,最好的处理方式是跳过当前已检查的。
结果
根据上述思路,代码如下:
public int singleNonDuplicate(int[] nums) {
int l = 0,r = nums.length-1;
while(l<r){
int mid = (l+r)/2;
//如果mid和前一个数相同,那么调整mid到后一个数开始
if(nums[mid-1] == nums[mid]) mid+=1;
//如果mid和后一个数相同,那么调整mid到后一个数开始
else if(nums[mid+1] == nums[mid]) mid+=2;
else return nums[mid];
//l到mid的长度是奇数,说明L->mid区间中出现了要求的值,这里需要跳过已检查的值
if(((mid-l)&1)==1) r = mid-3;
//l到mid的长度是偶数,说明mid->R区间中,出现了要求的值
else l = mid;
}
return nums[l];
}
AC
执行用时:0 ms, 在所有 Java 提交中击败了100.00%的用户
内存消耗:47.1 MB, 在所有 Java 提交中击败了10.25%的用户
总结
至目前,已知的数组解决问题的常见思路,新加了一个:
- 根据题目所求特征,从数组长度入手。
常见思路目前已用到的:
- 从数组本身特征入手(排序)
- 从数组长度入手