位运算

领略位运算之美

在阅读之前先补充一下计算机的二进制运算

逻辑运算:与,或,非,异或运算 XOR

  • 运算符号为&,运算规律是:真真为真,真假为假,假假为假

  • 运算符号为|,运算规律是:真真为真,真假为真,假假为假

  • 运算符为~,取反的逻辑,运算规律:二进制位若为1,取反后为0。若为0,取反后为1

  • 异或运算XOR

  1. 一个值与自身的运算,总是为 false。
x ^ x = 0
  1. 一个值与 0 的运算,总是等于其本身。
x ^ 0 = x

正数、负数的表示

对于负数的表示,比较特殊,其有一条运算规律:对非负数的二进制进行取反、然后+1,便可得其负数的二进制表示

加减法运算过程

  • 加法

类似十进制的进位方式,二进制逢二进一

  • 减法

    可以看作加法,5-3可以看作5+(-3)

练练手

136. 只出现一次的数字

给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。

class Solution:
    def singleNumber(self, nums: List[int]) -> int:
        res = 0
        for i in nums:
            res = res ^ i
        return res
260. 只出现一次的数字 III

给定一个整数数组 nums,其中恰好有两个元素只出现一次,其余所有元素均出现两次。 找出只出现一次的那两个元素。你可以按 任意顺序 返回答案。

方法一

与上面那道题不同的是一组里面有两个不同的数,那么我们除了用哈希还可以用位运算解决这个问题!

首先,有一点是非常确定的,那就是两个不重复数的二进制位一定有一位不同,也就是说异或完成的结果一定有一位是1。我们找出这一位,然后自己另外定义一个只有这一位是1、其它位都是0的数,用这个数和数组中每个数相与,如果相与结果为0,放到第一组,否则放到另外一组。每组的数字都全部进行异或,最后每组重复的数已经消掉,只剩下一个数,即不重复的数,这就把两个不重复的数分开了。

不过,大家回过头反思我们寻找第一次遍历后寻找异或结果中1的位置这个过程,可能会有更直接、更本质的操作,那就是:可以让它和它的相反数进行与操作。

一个正整数和它的相反数进行相与操作,结果中一定只含一个一,其他位位0,而且这个1的位置正好是这个正整数最后一个1的位置。

m = ero & -ero

然后每个数进行与运算,这样可以进行分成两组,那两组分别进行异或运算,自然可以找出这两个数啦!

class Solution:
    def singleNumber(self, nums: List[int]) -> List[int]:
        ero = 0
        for num in nums:
            ero ^= num
        m = ero & -ero
        res = [0] *2
        for num in nums:
            if m & num == 0:
                res[1] ^= num
            else:
                res[0] ^= num
        return res
方法二
  1. 因为a^a = 0,先遍历数组对所有数异或,得到两个只出现一次的数的异或结果eor
  2. 通过eor & - eor取得eor的最右边的1,令其为rightOne。因为两个数对应位不同异或后该位才会为1,所以rightOne对应位在两个数中一定是一个为0,一个为1,这是在为后面分组做准备
  3. 再遍历数组,每个数和rightOne与,将所有数分成与运算后为0和为1两组,要找的两个数分别在两组
  4. 把其中一组的所有数异或,得到其中一个要找的数res,另一个数即为res ^ eor
class Solution:
    def singleNumber(self, nums: List[int]) -> List[int]:
        eor = 0
        for num in nums:
            eor ^= num
        rightOne = eor & - eor
        res = 0
        for num in nums:
            if num & rightOne != 0:
                res ^= num
        return [res, res ^ eor]

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值