力扣题目——260. 只出现一次的数字 III

260. 只出现一次的数字 IIIicon-default.png?t=N7T8https://leetcode.cn/problems/single-number-iii/

        给你一个整数数组 nums,其中恰好有两个元素只出现一次,其余所有元素均出现两次。 找出只出现一次的那两个元素。你可以按 任意顺序 返回答案。

        你必须设计并实现线性时间复杂度的算法且仅使用常量额外空间来解决此问题。

示例 1:

输入:nums = [1,2,1,3,2,5]
输出:[3,5]
解释:[5, 3] 也是有效的答案。

示例 2:

输入:nums = [-1,0]
输出:[-1,0]

示例 3:

输入:nums = [0,1]
输出:[1,0]

提示:

  • 2 <= nums.length <= 3 * 104
  • -231 <= nums[i] <= 231 - 1
  • 除两个只出现一次的整数外,nums 中的其他数字都出现两次

 

这道题和上一篇博客中的题目类似,不过是做了点变形

思路一:
        这道题ans有两个值只出现一次,其余的数都出现两次,依然可以采用先看首尾再看中间的方法。

解题方法:
       1. 边界条件:nums数组大小为2,直接返回nums数组

       2. 定义ans数组存储两个答案值,首先要判断答案值是否位于首尾处,若在,则压入ans数组,之后要判断ans大小是否为2,若为2则表明两个值各在首尾,直接return;

       3. 若不足2,说明至少有一个答案位于nums数组中部,则需遍历nums数组,找到相邻连续不同的三个数,中间的数就是一个答案值,而结束遍历的条件就是当ans.size==2的时候,表明答案已全部找出。

Code
C++

class Solution {
public:
    vector<int> singleNumber(vector<int>& nums) {
        int n=nums.size();
        if(n==2) return nums;
        sort(nums.begin(),nums.end());
        vector<int> ans;
        if(nums[0]!=nums[1]) ans.emplace_back(nums[0]);
        if(nums[n-1]!=nums[n-2]) ans.emplace_back(nums[n-1]);
        if(ans.size()==2) return ans;
        //else ans未满
        for(int i=1;i<n-1;i++){
            if(nums[i-1]!=nums[i]&&nums[i]!=nums[i+1]){
                ans.emplace_back(nums[i]);
            }
            if(ans.size()==2) break;
        }
        return ans;
    }
};

上述代码提交之后是给通过了,但是代码中我使用了sort排序,理论上时间复杂度好像不满足线性要求,不太清楚原因。



思路二:
       1. 利用位运算中的异或操作——两者异或,同为0,不同为1。

       2. 我们知道0 ^ x = x,那么将数组中所有的数字异或,最终结果就是两个目标值ans1与ans2的异或值: nums[0]^nums[1]^...^nums[size-1] = ans1 ^ ans2;
通过异或我们可以将ans1和ans2的数区分开来(如 二进制下ans1第k位是1 但是ans2第k位是0)

       3. 现在我们重新思考,如果我们按照第k位是1/0的规则,将数组中的数进行一个划分,很明显,第k位是1的数中应当包含{x,x,y,y... ans1} ;同样,第k位是0的数中应当包含{a,a,b,b... ans2}。之后我们遍历整个nums数组,如果第k位是1,就与ans1异或,第k位是0,就与ans2异或,最终可以得到ans1和ans2的值。

Code
C++

class Solution {
public:
    vector<int> singleNumber(vector<int>& nums) {
        if(nums.size()==2) return nums;//边界
        int n=0;
        for(auto x:nums) n^=x;//n=ans1^ans2;
        int l=(n==INT_MIN?n:n&(-n));//l为n十六进制表示数最低位1的位置
        int ans1=0,ans2=0;
        for(auto x:nums){
            if(l&x) ans1^=x;//数x的l位是1
            else ans2^=x;//x的l位是0
        }
        return {ans1,ans2};
    }
};

        需要注意的是代码中l的求解方式,目的就是为了找出两个目标值二进制表示的不同之处。代码注释中说的不太确切,l解释:只保留二进制数n 最低位的1,对应的值就是l。

        这种解法效果还不错,主要是得打开思路,利用异或将数组分类,分别找出两个目标值。位运算在我们学计算机系统的时候经常用到,感觉解题中想一想能否利用位运算解题,万一更高效呢[手动狗头QAQ]

  • 17
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值