算法技巧:位运算

知识点

1. 概念

知识点

按位运算符是把数字看作二进制来进行计算的

2.操作

非操作 ~:把num的补码中的0和1全部取反(0变1,1变0),且符号也取反
与操作&:两个对应位都为1时才为1
或操作 |: 两个对应位中有一个1就为1
异或操作 ^ :不同时为1,相同时为0;满足交换律和结合律
按位左移操作<<:向左移动i位所得到的值,右移为>>

题目

1. 只出现一次的数字

只出现一次的数字

需要线性时间复杂度和不用额外空间;集合、哈希表存储都需要O(n)的空间。需要使用位运算
对于这道题,可使用异或运算 ⊕。异或运算有以下三个性质。

  1. 任何数和 0 做异或运算,结果仍然是原来的数,即 a⊕0=a。
  2. 任何数和其自身做异或运算,结果是 0,即 a⊕a=0。
  3. 异或运算满足交换律和结合律,即 a⊕b⊕a=b⊕a⊕a=b⊕(a⊕a)=b⊕0=b。

可推导为


异或运算
class Solution:
    def singleNumber(self, nums: List[int]) -> int:
        return reduce(lambda x, y: x ^ y, nums)

这里reduce 函数将一个数据集合(链表,元组等)中的所有数据进行下列操作,用传给 reduce 中的函数 function(有两个参数)先对集合中的第 1、2 个元素进行操作,得到的结果再与第三个数据用 function 函数运算,最后得到一个结果。

reduce([function], sequence, initial)


reduce

异或解法:异或运算满足交换律,aba=aab=b,因此ans相当于nums[0]nums[1]nums[2]nums[3]nums[4]..... 然后再根据交换律把相等的合并到一块儿进行异或(结果为0),然后再与只出现过一次的元素进行异或,这样最后的结果就是,只出现过一次的元素(0^任意值=任意值)

2. 只出现一次的数字 Ⅱ

image.png

二进制位思路:
答案的第 ii 个二进制位就是数组中所有元素的第 ii 个二进制位之和除以 33 的余数。

为了方便叙述,我们称「只出现了一次的元素」为「答案」。

由于数组中的元素都在 int(即 3232 位整数)范围内,因此我们可以依次计算答案的每一个二进制位是 0 还是 1。

具体地,考虑答案的第 i 个二进制位(ii 从 00 开始编号),它可能为 0 或 1。对于数组中非答案的元素,每一个元素都出现了 3 次,对应着第 ii 个二进制位的 3 个 0 或 3 个 1,无论是哪一种情况,它们的和都是 3 的倍数(即和为 0 或 3)。

image.png

因此,统计所有数字的各二进制位中 1 的出现次数,并对 3求余,结果则为只出现一次的数字。

3. 2的幂

2的幂

可知n的二进制位,最高位为1,其他为0



且n−1 二进制最高位为 0,其余所有位为 1;
则n与n-1的与运算则一直为1


class Solution:
    def isPowerOfTwo(self, n: int) -> bool:
        return n > 0 and n & (n - 1) == 0

4. 子集

image.png

回溯

class Solution:
    def subsets(self, nums: List[int]) -> List[List[int]]:
        res = []
        n = len(nums)
        
        def helper(i, tmp):
            res.append(tmp)
            for j in range(i, n):
                helper(j + 1,tmp + [nums[j]] )
        helper(0, [])
        return res  

你可能感兴趣的:(算法技巧:位运算)