1. 题目描述
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
说明:
你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?
示例 1:
输入: [2,2,1] 输出: 1 |
示例 2:
输入: [4,1,2,1,2] 输出: 4 |
2. 使用列表
首先考虑到使用列表的数据结构,因为append方法每次的操作均为常数级的操作。但在检索的过程中,in方法仍是线性级别的,导致运行结果为“超出时间限制”。
class Solution:
def singleNumber(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
sn = []
for nu in nums:
if nu not in sn:
sn.append(nu)
else:
sn.remove(nu)
return sn[0]
3. 使用字典
列表的in方法采用的仍是遍历的过程,此时会想到字典这个数据结构。生成键-值对,其中关键字作为键,出现次数为值。使用keys方法就可以轻松的得到键的可迭代对象,这样对关键字的检索就是常数级了。在这里,输出使用的pop方法,输入采用的基本赋值。经测试显示“通过”。
class Solution:
def singleNumber(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
sd = {}
for nu in nums:
if nu in sd.keys():
sd.pop(nu)
else:
sd[nu] = 1
return list(sd.keys())[0]
需要注意的地方是:当字典中的键值需要导出的时候,使用keys方法得到的是可迭代对象,所以用list方法列表化。
4. 使用集合
集合的思想就是简化出来字典的“键”,在这里,输入采用的add方法,输出采用的remove方法。返回值的int类型采用的pop方法。平台上测试的结果,是“通过”,但要比使用字典的数据结构要慢。
class Solution:
def singleNumber(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
sd = set()
for nu in nums:
if nu in sd:
sd.remove(nu)
else:
sd.add(nu)
return sd.pop()
5. 如何不使用额外空间实现?
时间复杂度的线性级别很容易采用数据结构的特性来弥补,但空间复杂度的常数级,也就意味着只能使用数个中间变量作为中转来实现。
首先想到的是加减操作,但运算符的判定是要建立在有序数组的基础上。所以可以采用先排序,再设置标志位flag进行加减的操作。函数sorted()采用的是就地操作,一定程度上也算是没有添加额外的辅助空间。经测试,显示“通过”。
class Solution:
def singleNumber(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
sn, flag = 0, True
for nu in sorted(nums):
if flag:
sn += nu
flag = False
else:
sn -= nu
flag = True
return sn
那有没真正意思上的不使用额外空间的线性级别的算法呢?是有的,这里是借助异或运算符的魔法。异或运算符在二进制的位数上,相同则为0,不同为1,常用于不设置中间变量传递值就交换两个数。
在这里,异或运算符的经典之处,在于存储二进制各位上的状态值。相同的情况下就会抵消,留出来的就是单个的。比如:
数组:【1, 3, 2, 1, 2】 | 1:0 0 1
扫描二维码关注公众号,回复:
3754006 查看本文章
3:1 0 1 2:0 1 0 1:0 0 1 2:0 1 0 |
竖向看的话,明显的相互抵消,尽管有先后顺序,但值并没有发生变化。
class Solution:
def singleNumber(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
sn = 0
for nu in nums:
sn ^= nu
return sn