JZ40 剑指offer 数组中只出现一次的数字

第40题 数组中只出现一次的数字

题目描述

一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。

class Solution {
public:
    void FindNumsAppearOnce(vector<int> data,int* num1,int *num2) {
        int ret =0 ;
        for(const int k : data){
            ret ^= k;
        }
        ret &= (-ret);
        *num1 = 0, *num2 = 0;
        for(const int k : data){
            if(k & ret) *num1 ^= k;
            else *num2 ^= k;
        }
    }
};
class Solution:
    def singleNumbers(self, nums: List[int]) -> List[int]:
        ret = 0
        for n in nums:
            ret ^= n
        ret &= -ret
        n1 = n2 = 0
        for n in nums:
            if n & ret:
                n1 ^= n
            else:
                n2 ^= n
        return [n1, n2]

思路:
利用位异或的性质
n^0 = n;
n^n = 0;
n^ n^ m = n^ (n^m) 满足交换律
位异或的位值取决于该位上1的个数的奇偶,奇数为1,偶数为0。
因为其他出现两次,位异或就抵消掉只有我们要的两个数字,得到的结果ret是两个不同数字的位异或的结果。
既然是两个不同的数字,那么这两个数字肯定在某一位上存在不同值情况,即一个在该位置是1,另一个是0,这个位置肯定是ret等于1的位置(因为不同值异或结果为1),为了找到这个位置,我们将ret与-ret作位与操作,因为补码的关系,负数是整数的除符号位的按位取反加1,比如0010,1110,我们就可以找到ret从左往右第一个为1的位置,位与结果就是该位置为1,其余位置为0。
于是我们将这些数分为两类,一类该位置为1,另一类为0,再做异或操作就可以分别得到这样两个值了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值