逻辑运算 之 异或的妙用
逻辑运算 之 异或的妙用
文章目录
😉 第一节先复习一下异或的基本知识,如果认为已经了解就不用看啦,可以直接跳到异或应用部分 ~
1. 异或基本知识
2. 异或的应用
例1: 十进制数值的异或运算 5 ⊕ 3 = ?(这道题很简单,给大家预热一下~)
- 首先将数值都转换为二进制- 按位进行异或运算
5(10) = 0101(2) 0101 ⊕ 0011 = 0110(2)
0110(2) = 6(10)
所以:5 ⊕ 3 = 6
例2: 交换两个变量的数值,要求不使用辅助存储空间。
说明: 由于要求不使用额外的存储空间,所以就否决了常规的设置 temp 的方法。
提示: 利用异或的性质:若 A ^ B = C,则 B ^ C = A。
解答: 假设变量 a = 2,b = 5,则 a ^ b = w(先用额外的 w 帮助理解),此时 w 其实就是进行 a 和 b 交换的中间人,要让 b 的值等于 a,只需要 b = b ^ w,同样地,要让 a 的值等于 b,就是 a = a ^ w。
进一步,我们略去中间变量 w,直接令 a = a ^ b,此时 a 相当于 w,那么 b = b ^ a,a = a ^ w = b ^ w = b ^ a(注意这一步,此时变量 b 的值就是我们需要的 a,所以 b 代替了 a,而变量 a 实际上就是 w)。
(也许我表达能力不是很好,所以麻烦大家自己动手划拉划拉,就很好懂啦~ 可以用例1计算十进制数异或的方法,验证一下是不是这样子的)
// C 代码实现
#include <stdio.h>
void swap(int &a, int &b);
void swap(int &a, int &b)
{<!-- -->
a = a ^ b;
b = b ^ a;
a = b ^ a;
}
int main()
{<!-- -->
int x = 2, y = 5;
swap(x, y);
printf("x = %d, y = %d\n", x, y); // x = 5, y = 2
return 0;
}
例3: 1 ~ 1000 放在含有 1001 个元素的数组中,只有唯一一个元素重复出现了2次,找出这个重复的数字。要求不能使用辅助存储空间并且数组的每个元素只能访问一次。
说明: 要求不使用额外的存储空间,并且只能访问一次数组元素,所以不能用 for 循环啦,这样代价也比较大。
提示: 和【例2】一样,利用异或的性质:若 A ^ B = C,则 B ^ C = A。
解答: 设 n 为重复的数字, 由 A ^ B = C 则 B = A ^ C 知:
n = T ^ ( 1 ^ 2 ^ 3 ^ … ^ n-1 ^ n ^ n+1 ^ … ^ 1000 )
所以,只需要先计算给定数组所有元素的异或(即T),然后再在计算1~1000所有元素的异或(即 1 ^ 2 ^ 3 ^ … ^ n-1 ^ n ^ n+1 ^ … ^ 1000),最后将这两个结果再异或一次就OK了。
# python 代码实现
from functools import reduce
def double(list):
ret = reduce(lambda x,y: x^y, list) # T
tmp = [i+1 for i in range(1000)] # 1~1000
tmpx = reduce(lambda x,y: x^y, tmp)
n = ret ^ tmpx
return n
例4: 一个数组存放若干整数,一个数出现奇数次,其余数均出现偶数次,找出这个出现奇数次的数。
所有出现次数为偶数次的进行异或后等于 0,所以直接对整个数组进行异或,结果就是出现奇数次的数。
def odd(list):
ret = reduce(lambda x,y: x^y, list)
return ret
例5: 一个整型数组 nums 里除两个数字之外,其他数字都出现了两次。请找出这两个只出现一次的数字。要求时间复杂度是O(n),空间复杂度是O(1)。LeetCode 直达
说明: 来自剑指offer的一道题 解答: 由异或性质可知: 2)题给数组内所有数异或 = 两个单独的数异或
如果能将两个单独的数分别放在两个数组里,且成对儿的数在同一个数组内,然后分别对两个数组求异或,就可以得到两个单独的数。
为了满足1)两个单独的数分别在两个数组,2)成对儿的数在同一个数组,可以对单独的两个数求异或,然后随便挑一个不为 0 的位置作为依据划分数组,就可以达到上面两个要求。假设数组异或的二进制结果为 101,说明两个单独的数从右向左第一位不同,所以就根据数组里所有数的第一位来划分。
# python 代码实现
from functools import reduce
class Solution:
def singleNumbers(self, nums: List[int]) -> List[int]:
ret = reduce(lambda x, y: x ^ y, nums) # 整个数组异或 = 两个单独数的异或
div = 1
while div & ret == 0:
div <<= 1 # 从右向左找第一个不为0的位置
a, b = 0, 0
for n in nums:
if n & div:
a ^= n
else:
b ^= n
return [a, b]