只出现一次的数字

题目

136. 只出现一次的数字 - 力扣(LeetCode)

剑指 Offer 56 - I. 数组中数字出现的次数 - 力扣(LeetCode)

解题思路

异或性质:
0 ⊕ 0 = 0 (1) 0 \oplus 0=0 \tag{1} 00=0(1)
0 ⊕ 1 = 1 (2) 0 \oplus 1=1 \tag{2} 01=1(2)
1 ⊕ 0 = 1 (3) 1 \oplus 0=1 \tag{3} 10=1(3)
1 ⊕ 1 = 0 (4) 1 \oplus 1=0 \tag{4} 11=0(4)

结论 ( 1 ) : (1): (1) ( 1 ) ( 2 ) (1)(2) (1)(2)知,与 0 0 0异或等于本身

结论 ( 2 ) : 由 (2):由 (2)(3)(4)$知,与 1 1 1异或等于取反

结论 ( 3 ) : 由 (3):由 (3)(1)(4)$知,相同异或为 0 0 0

结论 ( 4 ) : 由 (4):由 (4)(2)(3)$知,相异异或为 1 1 1

同时异或拥有
交换律: a ⊕ b = b ⊕ a a \oplus b=b \oplus a ab=ba
结合律: ( a ⊕ b ) ⊕ c = a ⊕ ( b ⊕ c ) (a \oplus b) \oplus c=a \oplus (b \oplus c) (ab)c=a(bc)

第一题:假如数组中所有数为 a 1 , b 1 , . . . , a n s , . . . , a n , b n a_1,b_1,...,ans,...,a_n,b_n a1,b1,...,ans,...,an,bn
a i , b i a_i,b_i ai,bi为相等的一对,成为对子。
所有数相异的结果可以看做:利用交换律和结合律可以将所有对子先异或,结果为零,最后异或 a n s ans ans,根据结论 ( 1 ) (1) (1),因此结果为 a n s ans ans

第二题:假如数组中所有数为 a 1 , b 1 , . . . , a n s 1 , a n s 2... , a n , b n a_1,b_1,...,ans1,ans2...,a_n,b_n a1,b1,...,ans1,ans2...,an,bn
利用第一题的思想,只需设法将数组分为两组,其中 a n s 1 , a n s 2 ans1,ans2 ans1,ans2各在一组。
因为其他对子的值相同,只要按照同一标准分组,对子肯定会分在同一组,从而异或为零,因此不必考虑分组标准对其他元素的影响,只需考虑如果将 a n s 1 , a n s 2 ans1,ans2 ans1,ans2分开。

假设: m a s k = a n s 1 ⊕ a n s 2 mask=ans1 \oplus ans2 mask=ans1ans2
如果 m a s k mask mask为零,这不可能,根据结论 ( 3 ) (3) (3),与题设矛盾。

如果 m a s k mask mask不为零, m a s k mask mask的二进制表示中一定有一位为 1 1 1,假设为 m a s k i mask_i maski
根据结论 ( 4 ) (4) (4) a n s 1 i , a n s 2 i ans1_i,ans2_i ans1i,ans2i一定不相同。由此找到将 a n s 1 , a n s 2 ans1,ans2 ans1,ans2分开的标准。

理论上根据 m a s k mask mask中任意不为零的 b i t bit bit位都可以将 a n s 1 , a n s 2 ans1,ans2 ans1,ans2区分开。
为了简便,选择 l o w b i t lowbit lowbit

代码

class Solution {
public:
    vector<int> singleNumbers(vector<int>& nums) {
        int mask = 0;
        vector<int> ans;
        int a = 0;
        int b = 0;

        for(auto& t : nums){
            mask ^= t;
        }

        // lowbit
        mask = mask & (~mask + 1);

        for(auto& t : nums){
            if(t & mask){
                a ^= t;
            }else{
                b ^= t;
            }
        }
        ans.push_back(a);
        ans.push_back(b);

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值