0
点赞
收藏
分享

微信扫一扫

异或(^)的含义与基本用法

异或(^)的含义与基本用法

异或的含义

​ 异或(^)和与运算(&)、或运算(|)都是位运算,因为计算机的运算都是将数据转换成二进制来进行的,所以一般来说,位运算比加减乘除的算数运算快得多。异或的运算法则如下:

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

相同为0,不同为1

异或的运算性质

​ 异或有几大运算性质:

  • 交换律: ab==ba

  • 结合律: aba==aab

  • 任何非0的数和0异或的结果都是它本身

  • 任何数和自己本身异或的结果都为0

    如果你看到这里,感觉上面列的运算法则和性质有所冲突的时候,请考虑进制不同的情况。本质上异或是在二进制层面上的,这是它的运算法则,当直观表现在十进制上的,才是它的运算性质。

​ 在二进制层面上,异或的运算可以看成是无进位相加,举个例子

	1010110
  	1100011
 ^	0110101

​ 相加的两个二进制数,从右往左数第二位上都是1,1+1本来应该进位1,本位变成0的,然后^运算,无进位,所以结果直接写成0。这样的理解在后续的运用中能更加形象的帮我们解释一些结果。

​ 就此,异或的运算性质也概括完了,接下来让我们看看它的运用情况。

异或的运用

在需要交换两个变量的值时,你是否马上想到再设置一个中间变量来进行交换?那么如果我跟你说不用中间变量,两者可以直接交换呢?下面我们就用异或来实现

void swap(int a,int b)
{
	a=a^b;
	b=a^b;
	a=a^b;
}

这样三个格式一样的语句就完成了值的交换,是不是觉得很不可思议?下面我们来分析一下,加深对异或的理解。

在第一条语句结束后,分析一下a,b的值:a=a^b b=b;

在第二条语句结束后,分析一下:a不变,a=a^b 分析b我们先交换律,再结合律:b=ba=b(a^b);

这里我们对b的值进一步化简,因为性质——任何数跟它自己异或的结果都为0,所以b=a^0=a

最后分析第三条语句,a=ab=(ab)^a,同理,a=b;

就此,我们完成了a,b值的交换。


但需要注意的是,这种方法其实是抖机灵的做法,它的可读性很差。同时使用它有一个隐藏的限制条件,即交换的两值在内存空间中并不能属于同一区域,如排序数组中元素的值,因为当它们为同一性质的东西,即两者完全相同时,使用异或将会使它们都归零。

下面我们来看两个有关异或的实际问题:

Q1:在一堆数中,只有一种数,它出现了奇数次,而其他种的数,都出现了偶数次。请找出出现了奇数次的这种数。

直接上代码:

  int arr[20];
    int i,a;
    for(i=0;i<20;i++)
    {
        a^=arr[i];
    }
    //a就是出现奇数次的数

为什么这么说?结合两点,第一点,异或的性质,任何数和它本身异或的结果都是0;第二点,根据题意,除了需要找的这个数以外,其他数都能找到和它自己相同的数两两配对变成0。最后可以化简成该数^0,答案浮出水面。


Q2:在一堆数中,有且仅有两种数,出现了奇数次,而其他种的数,都出现了偶数次,请找出出现了奇数次的这两种数。

上代码:

int main()
{  
    int a=0,b=0;//设出现奇数次的数为a,b
    int t=0,i;
    int rightone;//二进制下,从右往左的第一个1
    int arr[20];

    for( i=0;i<20;i++)
    {
        t^=arr[i];
    }
    //现在的t==a^b,这两个出现奇数次的数异或

    //因为t==a^b;
    //a!=b
    //所以t的二进制肯定有一位上是1
    rightone=t&(~t+1);
    //一个数与(&)上自己取反+1的数,结果得到这个数二进制下最右侧的1
    //并且我们知道,a和b两者在这一位上,只有一者位上是1,另一个一定是0.
    for(i=0;i<20;i++)
    {
        if(t^arr[i]==0)//将该最右位是1和0的区分开
        {
            a^=arr[i];//此时a等于一种奇数次数^一些出现过偶数次数且该位也是1的数
                      //而出现偶数次可以抵消,所以直接得到了第一个出现了奇数次的数
        }
    }
    b=t^a;//由此再得到b
}

以上有两个难点,第一是得到一个数二进制下最右侧的1所在的位置,为该数&(~该数+1),即原码&补码,不明白的用几个二进制数试验即可,记住以后多可以拿来套用。

第二个是当我们用一个变量t异或了一轮数组后,得到了a^b,之后我们的第二轮异或,是有选择性的异或。如此便区分出了以下情况:

一侧另一侧
出现过奇数次的a出现过奇数次的b
出现偶数次且最右位为1的数出现偶数次且最右位为0的数

第二轮异或的精髓就在于将a,b分开了,而其他数都是出现过偶数次,根据自己^自己==0,全部抵消,所以最后就得到了单独的a和b。


感谢阅览,希望对你有帮助。

举报

相关推荐

0 条评论