逻辑运算 之 异或的妙用

逻辑运算 之 异或的妙用

文章目录

😉 第一节先复习一下异或的基本知识,如果认为已经了解就不用看啦,可以直接跳到异或应用部分 ~

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]
posted @ 2021-01-06 13:41  刘桓湚  阅读(597)  评论(0编辑  收藏  举报