巧用异或

异或规律

异或有以下规律

  1. 0^N = N
  2. N^N = 0
  3. 交换律 a ^ b = b ^ a
  4. 结合律 a ^ b ^ c = a ^ (b ^ c) = (a ^ b) ^ c
  5. 自反性 a ^ b ^ a = b

使用异或交换数据

一般的交换方式, 利用临时变量:

a = 1
b = 2

temp = a
a = b
b = temp

但你也可以使用异或的方法交换:

a = 1
b = 2

a = a ^ b  # 1 ^ 2
b = a ^ b  # 1 ^ 2 ^ 2 = 1
a = a ^ b  # 1 ^ 2 ^ 1 = 2

三次异或操作, 交换两个变量的值

注意: 异或交换变量时, 不能为引用类型, 否则为被清空

解决一些算法题

出现奇数次的 1 个数字

问题位置: 剑指 Offer II 070. 排序数组中只出现一次的数字
问题描述:
给定一个只包含整数的有序数组 nums ,每个元素都会出现两次,唯有一个数只会出现一次,请找出这个唯一的数字。

示例 1:
输入: nums = [1,1,2,3,3,4,4,8,8]
输出: 2

示例 2:

输入: nums = [3,3,7,7,10,11,11]
输出: 10

题解

非常简单, 由于异或的自反性(a ^ b ^ a = b), 所以只要将全部数异或, 那么得到的肯定是只出现一次的那个数

代码

class Solution:
    def singleNonDuplicate(self, nums: List[int]) -> int:
        eor = 0
        for i in nums:
            eor = eor ^ i
        return eor

出现奇数次的 2 个数字

题目位置: 剑指 Offer 56 - I. 数组中数字出现的次数
问题描述:
一个整型数组nums里除两个数字之外,其他数字都出现了两次。请写程序找出这两个只出现一次的数字。
要求时间复杂度是 O(n),空间复杂度是 O(1)。

示例 1:
输入:nums = [4,1,4,6]
输出:[1,6] 或 [6,1]

示例 2:
输入:nums = [1,2,10,4,1,4,3,3]
输出:[2,10] 或 [10,2]

题解

假设不同的两个数为ab
由于有两个数, 我们无法通过一次异或得到, 但是我们可以通过一次异或得到a ^ b
由于ab不可能相等, 那么, a ^ b != 0, 即 a ^ b的二进制位一定至少会有一个 "1"

# 如
arr = [1, 1, 2, 2, 3, 4]

# 全部异或得到 3 ^ 4 != 0

# 二进制位:
  011
  100
-------
  111

根据这一点, 我们可以把数据分为两半: 某一位有"1"和某一位无"1", ab就分别在这两半中
接下来, 只需要对这两半数据全部异或即可得到两个数ab

思路已经清晰了, 现在的重点要确定 "某一位" 时第几位, 我们需要提取出来, 不然没将数对半分
如何做呢? 记住 一个数最后位的 1 等于 一个数 与上 自己取反+1 即可, 下面是解释:

eor = 101011100
right_one = eor & (~eor + 1)

"""
eor = 101011100
~eor = 010100011
~eor+1 = 0101000100

& 0000000100
"""

代码

class Solution:
    def singleNumbers(self, nums: List[int]) -> List[int]:
        eor = 0
        # 同样先全部异或
        for i in nums:
            eor = eor ^ i

        # 确定eor最右边的1
        right_one = eor & (~eor + 1)

        eor2 = 0
        for i in nums:
            # 将数据分为两半, &right_one = 0 或 != 0
            if right_one & i == 0:
                #  继续异或, 得到第一个数
                eor2 = eor2 ^ i

        # eor ^ eor2 得到第二个数
        eor = eor ^ eor2
        return eor, eor2
posted @ 2022-01-10 17:36  403·Forbidden  阅读(300)  评论(0编辑  收藏  举报