找出数组中只出现一次的数(位运算的使用)

位运算的使用

题一:leetcode137 只出现一次的数字 II

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

采用统计出现次数的思想,重点是:当其出现三次应当当做其没有出现过。

两比特位即可标识三种状态,即两比特位即可标识数组中某元素出现0、1、2次三种状态。我们可以采用两个变量代表这两个比特位。这样做有一个好处:可以存储那个只出现一次的那个数值。

现在我们考虑数组中某元素的每个比特位的可能情况,即:

变量a变量b某元素的某个比特更新后的变量a更新后的变量b
00000
01001
10010
00101
01110
10200

上面考虑的是数组中某元素的某一个比特位可能的情况,数组中每个元素的每个比特位都进行上面情况的考虑,最终的变量b就是我们所求的那个只出现一次的数。

如果那个只出现一次的数出现了两次,那么最终的变量a就是那个只出现两次的数。

下面的代码我们考虑数组中所有元素出现5次,但只有一个数字出现1次。

nums = [1, 2, 4, 5] * 5 + [3] * 1

a, b, c = 0, 0, 0
for d in nums:
    a_ = (~a & b & c & d) + (a & ~b & ~c & ~d)
    b_ = (~a & ~b & c & d) + (~a & b & ~c & d) + (~a & b & ~c & ~d) + (~a & b & c & ~d)
    c_ = (~a & ~b & ~c & d) + (~a & b & ~c & d) + (~a & ~b & c & ~d) + (~a & b & c & ~d)
    a, b, c = a_, b_, c_
# 其中c就是只出现一次的数字3
print(a, b, c)
# 如果数字3出现2次,那么变量b就是这个只出现2次的数字
# 如果数字3出现3次,那么变量b和变量c就是这个只出现3次的数字
# 如果数字3出现4次,那么变量a就是这个只出现4次的数字

当然,我们可以在这道题的基础上继续延伸,比如数组中所有元素出现k次,只有一个元素出现n(0<n<k)次数,我们都可以采用上面的思路进行求解。当然我们需要的变量数量就需要m(2**m>=k)个。

题二:leetcode260 只出现一次的数字 III

给定一个非空整数数组,其中恰好有两个元素只出现一次,其余所有元素均出现两次。 找出只出现一次的那两个元素。

这题我们采用题一的思想是不可行的,因为我们所求的是两个数,两个数最终每个比特位上统计之后无法还原成两个数。

因为除了那个只出现一次的两个数,其余的数都出现了偶数次,所以我们可以采用两两抵消的思想进行,将出现偶数次的数字都抵消掉。如何才能让出现偶数次的数抵消掉呢?我们可以采用位的异或运算抵消掉出现偶数次的数。

最终,我们得到两个只出现一次的数的异或值,如何利用这个异或值得到最终的两个只出现一次的数呢?可以找到这个异或值为1的最位,然后通过这个位来区分两个数,通过遍历数组中所有的数,即可得到最终的只出现一次的两个数。

nums = [1, 2, 4, 5] * 2 + [3, 7]

tmp = 1
for num in nums:
    tmp ^= num

# 得到为1的最低位
# mask = 1
# while mask & tmp == 0:
#     mask <<= 1
mask = tmp & -tmp

x, y = 0, 0
for num in nums:
    if num & mask:
        x ^= num
    else:
        y ^= num
print(x, y)
# 如果上面出现奇数次的数只有一个,那么得到的x、y其中有一个为0

如上,我们通过这个方式即可求解类似的问题,只要出现偶数次数的数都可以采用异或抵消掉,然后即可通过两个或者一个出现奇数次的数的异或值,最终求解得到出现奇数次的两个数或者一个数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值