【图解算法数据结构】(八)位运算


目录

一、剑指 Offer 15. 二进制中 1 的个数

1.1 题求

1.2 求解

1.3 解答

二、剑指 Offer 56 - I. 数组中数字出现的次数

2.1 题求

2.2 求解

2.3 解答

三、剑指 Offer - II. 数组中数字出现的次数 II

3.1 题求

3.2 求解

3.3 解答

四、剑指 Offer 65. 不用加减乘除做加法

4.1 题求

4.2 求解

4.3 解答


一、剑指 Offer 15. 二进制中 1 的个数

1.1 题求

1.2 求解

法一:移位运算符

# 28ms - 97.36%
class Solution:
    def hammingWeight(self, n: int) -> int:
        # 汉明重量
        hw = 0
        for _ in range(32):
            # 若末位为 1, 则汉明重量 +1
            if n & 1 == 1:
                hw += 1
            # 右移一位 (左移也行 <<= )
            n >>= 1
        return hw

官方说明 

class Solution:
    def hammingWeight(self, n: int) -> int:
        res = 0
        while n:
            res += n & 1
            n >>= 1
        return res

# 28ms - 97.36%
class Solution:
    def hammingWeight(self, n: int) -> int:
        res = 0
        while n:
            res += 1
            n &= n-1  # 每轮消去数字 n 最右边的 1
        return res

1.3 解答

参考资料:

《剑指 Offer 第二版》

https://leetcode-cn.com/leetbook/read/illustration-of-algorithm/5vk1l3/

https://leetcode-cn.com/leetbook/read/illustration-of-algorithm/5vgz7m/


二、剑指 Offer 56 - I. 数组中数字出现的次数

2.1 题求

2.2 求解

法一:哈希集合

# 36ms - 98.58% - 可惜不符合要求
class Solution:
    def singleNumbers(self, nums: List[int]) -> List[int]:
        hashset = set()
        for n in nums:
            if n not in hashset:
                hashset.add(n)  # 未出现, 则加入
            else:
                hashset.remove(n)  # 出现过, 则移除
        return list(hashset)  # 最后剩下的就是仅出现过一次的

官方说明 

def singleNumber(self, nums: List[int]) -> List[int]:
    x = 0
    for num in nums:  # 1. 遍历 nums 执行异或运算
        x ^= num      
    return x;         # 2. 返回出现一次的数字 x

while z & m == 0:    # m 循环左移一位,直到 z & m != 0
    m <<= 1

for num in nums:
    if num & m: 
        x ^= num      # 若 num & m != 0 , 划分至子数组 1 ,执行遍历异或
    else: 
        y ^= num      # 若 num & m == 0 , 划分至子数组 2 ,执行遍历异或
return x, y           # 遍历异或完毕,返回只出现一次的数字 x 和 y

# 28ms - 99.96%
class Solution:
    def singleNumbers(self, nums: List[int]) -> List[int]:
        x, y, n, m = 0, 0, 0, 1
        for num in nums:         # 1. 遍历异或, 结果 n = x ^ y
            n ^= num
        while n & m == 0:        # 2. 循环左移,计算令 n == 1 用于划分子数组的辅助变量 m
            m <<= 1       
        for num in nums:         # 3. 遍历 nums 并根据辅助变量 m 分组
            if num & m:          # 4. 当 num & m != 0, 划分至子数组 1, 执行遍历异或
                x ^= num         
            else:                # 4. 当 num & m == 0, 划分至子数组 2, 执行遍历异或
                y ^= num         
        return x, y              # 5. 返回出现一次的数字 x, y

2.3 解答

参考资料:

《剑指 Offer 第二版》

https://leetcode-cn.com/leetbook/read/illustration-of-algorithm/eubbnm/

https://leetcode-cn.com/leetbook/read/illustration-of-algorithm/euj1a1/


三、剑指 Offer - II. 数组中数字出现的次数 II

3.1 题求

3.2 求解

法一:哈希集合 + 哈希表

# 40ms - 88.70%
class Solution:
    def singleNumber(self, nums: List[int]) -> int:
        hashset = set()
        hashmap = {}
        # 遍历数组
        for n in nums:
            # 首次记录
            if hashmap.get(n) is None:
                hashmap[n] = 0
                hashset.add(n)
            # 记录 +1
            hashmap[n] += 1
            # 出现次数为 3 则移除候选数
            if hashmap[n] == 3:
                hashset.remove(n)
        return hashset.pop()

法二:哈希表

# 52ms - 80.87%
class Solution:
    def singleNumber(self, nums: List[int]) -> int:
        hashmap = {}
        # 遍历数组
        for n in nums:
            # 首次记录
            if hashmap.get(n) is None:
                hashmap[n] = 0
            # 记录 +1
            hashmap[n] += 1
            # 出现次数为 3 则移除候选数
            if hashmap[n] == 3:
                del hashmap[n]
        # 最后剩下的一对 key:value 即为仅出现 1 次的数
        return hashmap.popitem()[0]

官方说明 

# 52ms - 80.73%
class Solution:
    def singleNumber(self, nums: List[int]) -> int:
        '''
        考虑数字的二进制形式,对于出现三次的数字,各二进制位出现的次数都是 3 的倍数
        因此,统计所有数字的各二进制位中 1 的出现次数,并对 3 求余,结果则为只出现一次的数字
        由于二进制只能表示 0, 1,因此需用两个二进制位来表示 3 个状态。设为 two, one
        '''
        ones, twos = 0, 0
        for num in nums:
            # 两个二进制位的状态转换
            ones = ones ^ num & ~twos
            twos = twos ^ num & ~ones
        return ones

# 388ms - 23.70%
class Solution:
    def singleNumber(self, nums: List[int]) -> int:
        counts = [0] * 32
        for num in nums:
            for i in range(32):
                counts[i] += num & 1  # 更新第 i 位 1 的个数之和
                num >>= 1             # 第 i 位 --> 第 i 位
        res, m = 0, 3
        for i in range(31, -1, -1):
            res <<= 1
            res |= counts[i] % m      # 恢复第 i 位
        return res if counts[31] % m == 0 else ~(res ^ 0xffffffff)

3.3 解答

参考资料:

《剑指 Offer 第二版》

https://leetcode-cn.com/leetbook/read/illustration-of-algorithm/9hyq1r/

https://leetcode-cn.com/leetbook/read/illustration-of-algorithm/9hctss/


四、剑指 Offer 65. 不用加减乘除做加法

4.1 题求

4.2 求解

官方说明

# 32ms - 78.97%
class Solution:
    def add(self, a: int, b: int) -> int:
        x = 0xffffffff
        a, b = a & x, b & x
        while b != 0:
            # n = a ^ b - 非进位和:异或运算
            # c = (a & b) << 1 - 进位:与运算 + 左移一位
            a, b = (a ^ b), (a & b) << 1 & x
        return a if a <= 0x7fffffff else ~(a ^ x)

4.3 解答

参考资料:

《剑指 Offer 第二版》

https://leetcode-cn.com/leetbook/read/illustration-of-algorithm/5vz6d1/

https://leetcode-cn.com/leetbook/read/illustration-of-algorithm/5vs8w7/

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值