排序数组中只出现一次的数字(剑指offer-70)

原题链接

题目描述

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

示例1

输入: nums = [1,1,2,3,3,4,4,8,8]
输出: 2

示例2

输入: nums = [3,3,7,7,10,11,11]
输出: 10

思路1

第一种解法用常规的异或,异或的性质:同位相同为0,不同为1。那么两个相同的数字异或的结果就是0了。所以我们只需要扫描一遍数组,把所有的数都异或起来,最后的异或值就是只出现一次的那个数了。时间复杂度为O(n)

class Solution {
    public int singleNonDuplicate(int[] nums) {
        int ans = 0;
        for(int num : nums){
            ans ^= num;
        }
        return ans;
    }
}

思路2

上一个思路虽然时间复杂度为O(n),已经算高效的了,但是依然不是最高效的解法,别忘了还有一个条件我们没用用到,有序数组。
看到有序,看到查找,我们可以想到用二分来做。
我们把所有数字进行分组,两个挨着的是一组,比如拿第二组 [3,3,7,7,10,11,11]来说,分好组就是(3,3) (7,7) (10,11) (11)
这样的话,在遇到单独出现的数字之前,前面的每个组里面的两个数字都是相同的,直到遇到单独出现的数组,后面所有的组也因为他的出现而都错开了位置,也变成了组内两个数字不相同,所以我们就需要找到第一个出现不同数字的那个组。

class Solution {
    public int singleNonDuplicate(int[] nums) {
        int l = 0, r = nums.length / 2;
        //l和r表示组号,n个数字被分成n/2组,其中0~n-1组都是两个数字,第n组是一个数字 	
        while(l <= r){
            int mid = (l + r) / 2;
            //i是第mid组第一个数字的下标
            int i = mid * 2;
            if(i < nums.length - 1 && nums[i] != nums[i + 1]){
            	//如果是第一组,因为左边没有组了,所以肯定是第一个出现不同的组
            	//如果不是第一组,但是他前面的那一个组两个数字相同,那么也可以说明他是第一组不相同的
                if(mid == 0 || nums[i - 2] == nums[i - 1]){
                    return nums[i];
                }
                r = mid - 1;
            }else{
                l = mid + 1;
            }
        }
        //如果前面的组都没出现不相同的组,那么结果就是最后那个自己一个组的数字
        return nums[nums.length - 1];
    }
}
上一篇:移除元素(remove,remove_if...unique...)


下一篇:Stream管道流的filter方法进阶