题目
难度:★★★★☆
类型:数组
方法:自动状态机
传送门
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现了三次。找出那个只出现了一次的元素。
说明:
你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?
示例
示例 1:
输入: [2,2,3,2]
输出: 3
示例 2:
输入: [0,1,0,1,0,1,99]
输出: 99
解答
方法1:统计
直接统计每个字符出现的频率,然后返回出现频率为1的数,需要空间复杂度。
from collections import Counter
class Solution0:
def singleNumber(self, nums):
return [k for k, v in Counter(nums).items() if v == 1][0]
方法2:自动状态机
这里比较推荐力扣官网的一个解答,传送门,空间复杂度为1。
class Solution:
def singleNumber(self, nums):
seen_once = seen_twice = 0
for num in nums:
seen_once = ~seen_twice & (seen_once ^ num)
seen_twice = ~seen_once & (seen_twice ^ num)
return seen_once
其实晦涩难懂,我们将完整的自动状态机实现出来给大家查看。
class Solution:
def singleNumber(self, nums):
"""
\0| \0| \0|
00 ---1--> 01 --1----> 10
^\__________1_________/
用00,01,10表示三个状态,即某一个数字出现的次数除以3的余数
00状态遇到激励1,变成01状态
01状态遇到激励1,变成10状态
10状态遇到激励1,变成00状态
每一种状态遇到激励0,保持当前状态不变,最后选取余数是1的情况,即01状态的后面一位作为结果
:param nums:
:return:
"""
def nums2bin(nums):
"""
将所有数字转换为等长的二进制字符串。
:param nums:
:return:
"""
nums = [bin(num)[2:] for num in nums]
l = max([len(num) for num in nums])
nums = [num.rjust(l, '0') for num in nums]
return nums
def transfer_one_bit(state, activation):
"""
根据激活值和状态转移规则,更新当前状态
:param state: 当前状态,用两位表示
:param activation: 当前激活值(遇到的数字)
:return: 更新后的状态
"""
if activation == '0':
return state
elif activation == '1':
if state == '00':
return '01'
elif state == '01':
return '10'
elif state == '10':
return '00'
else:
raise Exception
else:
raise Exception
def transfer_all_bit(current_result, num):
return [transfer_one_bit(cur_bit_state, act) for cur_bit_state, act in zip(current_result, num)]
state_result = ['00'] * len(nums) # 初始化每个位为状态零
for num in nums2bin(nums): # 遍历每个数字
state_result = transfer_all_bit(state_result, num) # 根据新的数字更新当前总状态
result = ''.join([state[1] for state in state_result]) # 取出来每个状态的第二位,因为只出现一次的时候这个位是1
return int(result, 2) # 转回十进制
如有疑问或建议,欢迎评论区留言~