Leetcode 题解 --二分查找--有序数组的 Single Element

[LeetCode] Single Element in a Sorted Array 有序数组中的单独元素

 

Given a sorted array consisting of only integers where every element appears twice except for one element which appears once. Find this single element that appears only once.

Example 1:

Input: [1,1,2,3,3,4,4,8,8]
Output: 2

Example 2:
Input: [3,3,7,7,10,11,11]
Output: 10

Note: Your solution should run in O(log n) time and O(1) space.

这道题给我们了一个有序数组,说是所有的元素都出现了两次,除了一个元素,让我们找到这个元素。如果没有时间复杂度的限制,我们可以用多种方法来做,最straightforward的解法就是用个双指针,每次检验两个,就能找出落单的。也可以像Single Number里的方法那样,将所有数字亦或起来,相同的数字都会亦或成0,剩下就是那个落单的数字。那么由于有了时间复杂度的限制,需要为O(logn),而数组又是有序的,不难想到要用二分搜索法来做。二分搜索法的难点在于折半了以后,如何判断将要去哪个分支继续搜索,而这道题确实判断条件不明显,比如下面两个例子:

1  1  2  2  3

1  2  2  3  3

这两个例子初始化的时候left=0, right=4一样,mid算出来也一样为2,但是他们要去的方向不同,如何区分出来呢?仔细观察我们可以发现,如果当前数字出现两次的话,我们可以通过数组的长度跟当前位置的关系,计算出右边和当前数字不同的数字的总个数,如果是偶数个,说明落单数左半边,反之则在右半边。有了这个规律就可以写代码了,为啥我们直接就能跟mid+1比呢,不怕越界吗?当然不会,因为left如何跟right相等,就不会进入循环,所以mid一定会比right小,一定会有mid+1存在。当然mid是有可能为0的,所以此时当mid和mid+1的数字不等时,我们直接返回mid的数字就可以了,参见代码如下:

解题思路:既然要logn复杂度就用二分查找,找出单一值的关键在于奇数偶数区间的划分

我们最后l 或者 h 要落到相等的那个值那里,所以while是 l < h, 最后返回的是nums[l],按理说nums[h]也是对的 但是有种情况

 

[1,2,2,3,3] 这个值在开头的情况你带进去h其实就是越界

class Solution {
    public int singleNonDuplicate(int[] nums) {
        int n = nums.length;
        int l = 0, h = n -1;
        while(l < h){
            int m = l + (h - l) / 2;
//看看后面有没有相等的
            if(nums[m] == nums[m + 1]){
                //看看是不是0-m之后的区间是奇数是偶数
                if((n - m - 1) % 2 == 1)
//前m个是奇数,就到前m个去找
                    h = m - 1;
//偶数就到后n-m个去找
                else l = m + 1;
            }
//剩下的就是跟后面的值不相等 那么就是 与前面的值相等或者不相等 
            else{
//看看有没有直接满足单值条件的
                if(m>=1 && nums[m] != nums[m - 1])
                    return nums[m];
                else{
//没有的话看前m个数的奇偶情况
                    if(m % 2 == 1)
//偶数
                        l = m + 1;
//奇数情况
                    else
                        h = m - 1;
                }
            }         
        }
        return nums[l];          
    }
}

简洁写法

令 index 为 Single Element 在数组中的位置。在 index 之后,数组中原来存在的成对状态被改变。如果 m 为偶数,并且 m + 1 < index,那么 nums[m] == nums[m + 1];m + 1 >= index,那么 nums[m] != nums[m + 1]。

从上面的规律可以知道,如果 nums[m] == nums[m + 1],那么 index 所在的数组位置为 [m + 2, h],此时令 l = m + 2;如果 nums[m] != nums[m + 1],那么 index 所在的数组位置为 [l, m],此时令 h = m。

因为 h 的赋值表达式为 h = m,那么循环条件也就只能使用 l < h 这种形式。

public int singleNonDuplicate(int[] nums) {
    int l = 0, h = nums.length - 1;
    while (l < h) {
        int m = l + (h - l) / 2;
        if (m % 2 == 1) {
            m--;   // 保证 l/h/m 都在偶数位,使得查找区间大小一直都是奇数
        }
        if (nums[m] == nums[m + 1]) {
            l = m + 2;
        } else {
            h = m;
        }
    }
    return nums[l];
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值