剑指 Offer II 070(力扣540):排序数组中只出现一次的数字(Java二分查找)

简介: 给定一个只包含整数的有序数组 nums ,每个元素都会出现两次,唯有一个数只会出现一次,请找出这个唯一的数字。

一、题目描述



给定一个只包含整数的有序数组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)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。


相关文章
|
2月前
|
存储 缓存 算法
Java 数组
【10月更文挑战第19天】Java 数组是一种非常实用的数据结构,它为我们提供了一种简单而有效的方式来存储和管理数据。通过合理地使用数组,我们能够提高程序的运行效率和代码的可读性。更加深入地了解和掌握 Java 数组的特性和应用,为我们的编程之旅增添更多的精彩。
33 4
|
2月前
|
存储 缓存 算法
提高 Java 数组性能的方法
【10月更文挑战第19天】深入探讨了提高 Java 数组性能的多种方法。通过合理运用这些策略,我们可以在处理数组时获得更好的性能表现,提升程序的运行效率。
36 2
|
2月前
|
存储 Java
Java“(array) <X> Not Initialized” (数组未初始化)错误解决
在Java中,遇到“(array) &lt;X&gt; Not Initialized”(数组未初始化)错误时,表示数组变量已被声明但尚未初始化。解决方法是在使用数组之前,通过指定数组的大小和类型来初始化数组,例如:`int[] arr = new int[5];` 或 `String[] strArr = new String[10];`。
91 2
|
2月前
|
Java
在 Java 中实现二分查找法
【10月更文挑战第9天】
34 1
|
2月前
|
Java
Java数组动态扩容和动态缩减
Java数组动态扩容和动态缩减
25 3
|
2月前
|
算法 Java
java冒泡排序与二分查找(详解)
java冒泡排序与二分查找(详解)
42 4
|
2月前
|
存储 Java 程序员
【一步一步了解Java系列】:何为数组,何为引用类型
【一步一步了解Java系列】:何为数组,何为引用类型
31 1
|
2月前
|
存储 算法 Java
带你学习java的数组军队列
带你学习java的数组军队列
36 0
|
4月前
|
搜索推荐 算法 Java
|
4月前
|
搜索推荐 算法 Java
经典排序算法之-----选择排序(Java实现)
这篇文章通过Java代码示例详细解释了选择排序算法的实现过程,包括算法的基本思想、核心代码、辅助函数以及测试结果,展示了如何通过选择排序对数组进行升序排列。
经典排序算法之-----选择排序(Java实现)