[面试][位运算]数组中仅出现1次的数字-套题收集

问题一:一个数字出现1次,其它出现偶数次

# 问题一 当奇数个数字唯一时:
from functools import reduce
def find_one(arr):
    return reduce(lambda x, y: x ^ y, arr)

问题二:两个数字出现1次,其它出现偶数次

# 问题二 当出现奇数个的数字为2个时:
#   主要思路为按位划分为两组,则每组各包含一个出现奇数次的数字,然后再根据异或运算的性质推算即可
def find_one_when_2(arr):
    # a ^ b = c 等价于 a^c = b
    c = find_one(arr)
    index = c & (~c + 1)  # 找到最右侧的1
    a = 0
    for i in arr:  # 按照在该位的01值划分数组
        if i & index:
            a ^= i
    a, b = a, c ^ a
    return (a, b) if a < b else (b, a)  # 返回不重复的数字(a, b), 升序排列

问题三:一个数字出现1次,其它出现3次

# 问题三 当除了一个数只出现一次,其它都出现三次时:
#   进行异或运算时,若某一位结果不为0,则该位不为1的数字出现了奇数次以上
#   为了区分出现一次的数字和出现三次的数字,使用两个位掩码:seen_once 和 seen_twice:
#       仅当 seen_twice 未变时,改变 seen_once。
#       仅当 seen_once 未变时,改变seen_twice。
# https://leetcode-cn.com/problems/single-number-ii/solution/zhi-chu-xian-yi-ci-de-shu-zi-ii-by-leetcode/
def one_and_three(arr):
    seen_once = seen_twice = 0
    for v in arr:
        # 第一次出现时,将v与seen_once相加
        # 第二次出现时,从seen_once中移除并赋值给seen_twice:
        # 第三次出现时: 从seen_twice中移除

        # 既不在出现一次、也不在出现两次的位掩码里面,我们就记录下来,出现了一次,再次出现则会抵消
        seen_once = ~seen_twice & (seen_once ^ v)  # 滤去出现了两次的位
        # 既不在出现两次的位掩码里面,也不再出现一次的位掩码里面(不止一次了),记录出现两次,第三次则会抵消
        seen_twice = ~seen_once & (seen_twice ^ v)  # 滤去出现了一次的位

    return seen_once

问题四:一个数字出现1次,其它出现m次

# 问题四 是上一题的加强版,当其它数字出现了m次
#   相比于哈希法,空间复杂度仅为O(1)
def one_and_more(arr, m):
    counts = [0] * 32
    for v in arr:  # 获取所有数字各二进制位的1出现次数
        for j in range(32):
            counts[j] += v & 1
            v = v >> 1
    res = 0
    for i in range(32):  # 将counts各元素对m求余,则结果为 “只出现一次的数字” 的各二进制位。
        res = res << 1
        res |= counts[31 - i] % m  # 恢复第 i 位的值到 res
    # 由于 Python 的存储负数的特殊性,需要先将 0 - 32 位取反(即 res ^ 0xffffffff ),再将所有位取反(即 ~ )。
    return res if counts[31] % m == 0 else ~(res ^ 0xffffffff)
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值