有序数组的单一元素

有序数组的单一元素

题目链接:540. 有序数组中的单一元素

题目描述:

给你一个仅由整数组成的有序数组,其中每个元素都会出现两次,唯有一个数只会出现一次。

请你找出并返回只出现一次的那个数。

你设计的解决方案必须满足 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

解法一:

因为该数组中除了那个单一元素之外,其他元素都是成对出现的,因此我们可以使用异或的方式,用一个变量与数组里面所有的数都异或一次,最后得到的结果就是那个单一元素。

x^x = 0,x^0 = x;

时间复杂度O(N)

空间复杂度O(1)

class Solution {
public:
    int singleNonDuplicate(vector<int>& nums) {
        //记录最终的答案
        int ans = 0;
        for(int i = 0;i<nums.size();i++)
        {
            ans^=nums[i];
        }
        return ans;
    }
};

解法二:

我们这道题还可以采用二分的方法来做。

我们可以这么想一下:在未存在这个单一元素之前,数组的数都是成对出现的, 因此如果不考虑“特殊”的单一元素的话,我们有结论:成对元素中的第一个所对应的下标必然是偶数,成对元素中的第二个所对应的下标必然是奇数。

当我们这个单一元素插入到数组中时,该单一元素的前半部分还是会有上面这个结论,但是单一元素的后半部分会因为该单一元素的插入而导致该结论翻转。从而有: 成对元素中的第一个所对应的下标必然是奇数,成对元素中第二个所对应的下标必然是偶数。

因此我们这道题的二段性就出来了,我们可以根据二分点mid的奇偶性来分情况讨论:

  • 如果mid下标是偶数,根据刚开始的结论,正常情况下偶数下标的值会与它后一个数的值相等。因此如果满足这个条件的话,证明单一元素不在mid的左边,而是应该在mid的右边不包括mid,因此我们更新区间为[mid+1,right],反之如果当前mid与它后一个数的值不相等的话,那么单一元素的值就应该在mid的左边包括mid,因此更新区间为[left,mid]
  • 如果mid下标是奇数,根据刚开始的结论,正常情况下奇数下标的值会与它前一个数的值相等。因此如果满足这个条件的话,证明单一元素不在mid的左边,而是应该在mid的右边不包括mid,因此我们更新区间为[mid+1,right],反之如果当前mid与它前一个数的值不相等的话,那么单一元素的值就应该在mid的左边包括mid,因此更新区间为[left,mid]

时间复杂度O(logN)

空间复杂度O(1)

class Solution {
public:
    int singleNonDuplicate(vector<int>& nums) {
        int n = nums.size();
        //特判一下
        if(n==1)
        {
            return nums[0];
        }
        //二分思路
        //在为存在这个单一元素之前,数组中的数都是成对出现的,因此成对出现的数 第一个数的下标是偶数第二个数下标是奇数
        //但是将这个单一元素插入到某个位置之后,该单一元素的前半部分数据还是会保持第一个数的下标是偶数,第二个数的下标是奇数
        //但是在该单一元素的后半部分数据这个性质就会改变
        //后半部分的数据会变成:成对数据的第一个数的下标是奇数,第二个数的下标是偶数
        int left = 0;
        int right = n-1;
        while(left<right)
        {
            int mid = left+(right-left)/2;
            //如果mid下标的值是偶数
            if((mid&1)==0)
            {
                //如果mid下标值与mid+1下标的值是相等的
                //证明只出现一次的数出现在mid的右边不包括mid
                //因此更新区间为[mid+1,right]
                if(mid+1<n&&nums[mid]==nums[mid+1])
                {
                    left = mid+1;
                }
                //否则更新区间为[left,mid]
                else
                {
                    right = mid;
                }
            }
            //如果mid下标的值是奇数
            else
            {
                //如果mid下标值与mid-1的值是相等的
                //证明只出现一次的数字在mid的右边不包括mid
                //因此更新区间为[mid+1,right]
                if(mid-1>=0&&nums[mid-1]==nums[mid])
                {
                    left = mid+1;
                }
                //否则更新区间为[left,mid]
                else
                {
                    right = mid;
                }
            }
        }

        return nums[right];
    }
};
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值