异或在算法中的妙用

异或在算法中的妙用

异或的性质

  • 与0异或为自己,与自己异或为0
x ^ 0 = x;
x ^ x = 0;
  • 满足交换律和结合律
x ^ y = y ^ x;
x ^ y ^ z = x ^ (y ^ z);

由以上两个性质可以得知:

  • 异或运算的结果与参与异或运算的变量的排列组合无关,即与运算的先后顺序无关

用异或来实现两个数值交换

例如:

int a = 11;
int b = 24;
a = a ^ b;		//该句执行完,得到: a = 11 ^ 24; b = 24;
b = a ^ b;		//得到: a = 11 ^ 24; b = 11 ^ 24 ^ 24 = 11 ^ 0 = 11;
a = a ^ b;		//得到: a = 11 ^ 24 ^ 11 = 0 ^ 24 = 24; b = 11;
//可见最终 a = 24;   b = 11;   实现了交换

代码如下:

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

注意的点

  • 上述示例中的 ab 必须是内存地址不同的值。

例如:

我们来交换一个数组中的两个值:

void swap(int arr[], int i, int j) {
	arr[i] = arr[i] ^ arr[j];
	arr[j] = arr[i] ^ arr[j];
	arr[i] = arr[i] ^ arr[j];
}

ij 指向了数组中的同一处,最后该处结果为 0

  • 对于C++中基本类型,只适用于 int char bool wchar_t(short int)
  1. 所以对于复杂数据的交换,不推荐使用异或,可以用引用或者指针来做;
  2. 还有一点,用异或不见得比定义 temp 传值更“快”,可参考博客:
    异或交换真的比开一个tmp快吗?_Lawliet-CSDN博客

异或运算的例题

对于一个数组:

  1. 已知只有一种数(记为 a )出现了奇数次,其他的所有数出现了偶数次,求 a
  2. 已知有两种数(记为 ab )出现了奇数次,其他的所有数出现了偶数次,求 ab

要求时间复杂度 O(N),空间复杂度 O(1)。

  1. 第一种情况:

    很容易想到将数组中的所有值异或运算,最后的结果即为 a

  2. 第二种情况:

    例如:

    int arr[] = { 1,1,1,2,3,3,4,4,5,5 };
    

    那么两个数就是 1 和 2

    • 同第一种情况,先将所有的数异或,最后得到结果记为 eor ,显然 eor = a ^ b,且 eor ! = 0
    • 对于 eor ,其底层01流中肯定不全为 0,我们假设第 k 位为 1 ,那么对于abk 位一个是1,另一个是0;
    • 显然,除却 ab ,对于数组中 第 k 位是 1(或者0) 的元素,它们是出现了偶数次;
    • 将 第 k 位是 1(或者0) 的元素进行异或,最后得到结果记为 eor1,那么 eor1 = a 或者是 eor1 = b,总能得到 ab 中的一个;
    • 最后将 eor1eor 异或 得到另一个。

    代码如下:

    void eorSolution(int arr[], int size) {
    	int eor = 0,eor1 = 0;
    	for (int i = 0; i < size; i++) {
    		eor ^= arr[i];
    	}
    	int rightOne = eor & (~eor + 1);
    	for (int i = 0; i < size; i++) {
    		if ((arr[i] & rightOne) == 0) {
    			eor1 ^= arr[i];
    		}
    	}
    	cout << eor1 << " " << (eor ^ eor1) << endl;
    }
    

    🔴 其中需要注意的是,提取出某数01流中最后出现的 1

    int rightOne = eor & (~eor + 1);
    

    原理是 将数 取反+1与原值与运算,前提是该数不为 0

🎸写在后面:

  • 算法部分需要画图理解,但是我还没找打合适的方式,画图部分后续会练习的。
  • 博客主要是为了让自己理解记忆,盘清逻辑,所以没有举例讲解,见谅啦😄
  • 学无止境,后续待改进……
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值