只出现一次的数字系列(位运算题解)
136. 只出现一次的数字
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素(使用常数空间)。
思路:
1、结合律:(a ^ b) ^ c = a ^ (b ^ c)
2、对于任何数x
,都有x ^ x = 0,x ^ 0 = x
初始化一个元素a=0
,与所有元素做一次异或,即可得到只出现一次的元素
代码:
class Solution:
def singleNumber(self, nums: List[int]) -> int:
a = 0
for num in nums:
a ^= num
return a
137. 只出现一次的数字 II
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现了三次。找出那个只出现了一次的元素。(使用常数空间)
思路:
基本思路还是借助异或运算来消除相同元素,但是由于每个元素出现了三次,需要另外借助一个变量b。
数组中的数字出现的次数为1或3;为了找出出现一次的数字,出现三次的数字异或运算之后,结果必须为0,出现一次的数字经过异或运算之后结果是他本身。用代码表示:
if b == 0:
if n == 0:
a= a
if n == 1:
a= ~a
if b== 1:
a= 0
代码:
class Solution:
def singleNumber(self, nums: List[int]) -> int:
a, b = 0, 0
for num in nums:
a = a ^ num & ~b
b = b ^ num & ~a
return a
260. 只出现一次的数字 III
给定一个整数数组 nums,其中恰好有两个元素只出现一次,其余所有元素均出现两次。 找出只出现一次的那两个元素。(使用常数空间)。
思路:
和136题的区别在于这个数组有两个只出现一次的数,所以可以借鉴。关键就是把数组分成两个部分,每个部分包含一个只出现一次的数字。
那么怎么把数组分成两部分呢?
这里数组所有元素都做一次异或运算的话,得到的结果是两个只出现一次的数字的异或结果s。要把这两个数字分开,可以在s的二进制表示中找到一个1,因为异或相同为0,相异为1,意味着这两个只出现一次的数的二进制表示是不同的,根据这个就可以把数组分成两个部分,至于其他出现两次的数,也按照这个规则分成两拨,不会影响结果。
那么,怎么找到异或结果中的1呢?
这里使用了s&(-s)
得到二进制数s最低位的1,其他位置零.至于为啥是这样(我也是看了其他题解的,反正我想不到)
得到划分数组的那个数之后,遍历数组,把其中一类做异或运算得到的结果就是一个只出现一次的数;再把它和两个只出现一次的数 的异或结果(就是原数组元素的异或结果)做一次异或运算,根据异或运算的结合律,可以得到另一个只出现一次的数。
代码:
class Solution:
def singleNumber(self, nums: List[int]) -> List[int]:
a = 0
for num in nums:
a ^= num
diff = a & (-a) # 保留二进制数a最低位的1
b = 0
for num in nums:
if diff & num: # 相当于把原数组分组
b ^= num
return [b, b^a]