异或运算符

异或

表示当两个数的二进制表示,进行异或运算时,当前位的二进制相同为0,不同为1.

表示为:

  • 0 ^ 0 = 0
  • 1 ^ 0 = 1
  • 0 ^ 1 = 1
  • 1 ^ 1 = 0

特点

  1. 0异或任何数,是任何数;
  2. 1异或任何数,任何数取反;
  3. 任何一个数字异或自己都等于0

面试题:一个整型数组中除了两个数字之外,其他的数字都出现了两次。试找出这两个只出现一次的数字。《剑指offer》


例如,数组{1,2,3,4,5,6,4,3,2,1} 中,5和6只出现了一次,其他都出现了两次。
这个问题想了很长时间,一直无解。
题中提到有两个出现一次的数字,先尝试解决数组中只有有一个出现一次的数字的。

比如,

 1 ^ 2 = 3
 1 ^ 3 = 2
 2 ^ 3 = 1
 1 ^ 1 = 0
 2 ^ 2 = 0
 3 ^ 3 = 0

这里利用异或的第三个特点,任何一个数字异或自己都等于0.我们从头到尾依此异或数组中的每一个数字,那么最终的结果刚好是那个只出现一次的数字,因为那些成对出现的数字全部都在异或中抵消了。

如,{1,2,3,1,2},1 ^ 2 ^ 3 ^ 1 ^ 2 = 3.
现在找只出现一次的两个数字,如{1,2,3,4,3,1,6,2,5},
1 ^ 2 ^ 3 ^ 4 ^ 3 ^1 ^ 6 ^ 2 ^ 5 = 5 ^ 6.

从头到尾依次异或数组中的每一个数字,那么最终得到的结果就是两个只出现一次的数字的疑惑结果。因为其他数字都出现了两次,在异或中全部抵消了。

这里写图片描述

由于两个数字不一样,异或结果不为0,也就是说在这个结果数字的二进制中至少有一位为1.
如上图,以第二位1为标准(第一位1也满足),把原数组中的数字分为两个子数组,第一个子数组中每个数字的第二位都是1,而第二个子数组中每个数字的第二位都是0.

由于我们分组的标准是数字中的某一位是1还是0,那么出现了两次的数字一定会被分配到同一个子数组。这样每个子数组都包含一个只出现一次的数字,而其他数字都出现了两次。

//代码实现
unsigned int Find_First_bitIs_1(int num)
{
    int index = 0;
    while(((num & 1) == 0) && (index < 8*sizeof(int)))
    {
        num = num >> 1;
        ++index;
    }
    return index;
}
bool IsBit_1(int num, unsigned int index)
{
    num = num >> index;
    return (num & 1);
}
void FindNum_Once(int array[], int length, int* num1, int* num2)
{
    if(array == NULL || length < 2)
        return;
    int res_OR = 0;
    for(int i=0; i < length; ++i)
        res_OR ^= array[i];

    unsigned int index = Find_First_bitIs_1(res_OR);

    *num1 = *num2 = 0;
    for(int j=0; j < length; ++j)
    {
        if(IsBit_1(array[j], index))
        {
            *num1 ^= array[j];
        }
        else
        {
            *num2 ^= array[j];
        }
    }
}

实现交换两个数

void Swap(int* a, int* b)
{
    *a ^= *b ^= *a ^= *b;
}
int main()
{
    int a = 10;
    int b = 20;
    Swap(&a, &b);
    printf("%d,%d\n",a,b);
    return 0;
}

使特定位反转

例如,有01011011,想要高四位取反,可以将它和11110000进行 ^ 运算,
01011011 ^ 11110000 = 10101011
与0相^保留原值。

参考资料:
《程序员的自我修养》
《剑指offer》

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值