[LeetCode]有序数组中的单一元素

489 阅读2分钟

这是我参与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%的用户

总结

至目前,已知的数组解决问题的常见思路,新加了一个:

  • 根据题目所求特征,从数组长度入手。

常见思路目前已用到的:

  • 从数组本身特征入手(排序)
  • 从数组长度入手