LeetCode 136.只出现一次的数字
剑指Offer 56.数组中只出现一次的数字
LeetCode 136.只出现一次的数字
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
说明:
算法应该具有线性时间复杂度。 要求不使用额外空间来实现。
输入:[2,2,1]
输出:1
输入:[4,1,2,1,2]
输出:4
解题思路
哈希表
以元素值为key,元素出现的频次为value,构建字典,最后返回value == 1的key即可。
这个方法最大的缺点在于占用了O(n)的辅助空间,因此不满足题目要求。
class Solution:
def singleNumber(self, nums: List[int]) -> int:
dic = {}
for i in range(len(nums)):
if nums[i] not in dic:
dic[nums[i]] = 1
else:
dic[nums[i]] += 1
for key, value in dic.items():
if value == 1:
return key
return None
位运算(异或)
利用位运算解决此题是较优的做法。
异或运算满足以下三个定律:
- 交换律:a ^ b ^ c <=> a ^ c ^ b
- 任何数于0异或为任何数 0 ^ n => n
- 相同的数异或为0: n ^ n => 0
因此在题例中,4 ^ 1 ^ 2 ^ 1 ^ 2 = 1 ^ 1 ^ 2 ^ 2 ^ 4 = 0 ^ 2 ^ 2 ^ 4 = 0 ^ 0 ^ 4 = 4
class Solution:
def singleNumber(self, nums: List[int]) -> int:
a = 0
for num in nums:
a = a ^ num
return a
剑指Offer 56.数组中只出现一次的数字
一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。
解题思路
与上题不同,上题只有一个数字出现一次,而这题是有两个数字出现一次。
继续考虑用位运算来处理,核心思想是将数组分为两半再进行上题的操作:
- 和上题一样,先将所有的数字进行异或,最后肯定得到两个只出现一次的数字异或的结果;
- 对这个结果进行分析,找到其二进制为1的最低位,这个位上为1,说明两个数字的二进制在这个位上不相同,必然一个为1,一个为0;
- 可以根据在此位上是否1,将数组里的所有数分为两半,每边都为若干对成对的数字加上一个只出现一次的数字;
- 最后在两边各自按上题异或即可。
class Solution:
# 返回[a,b] 其中ab是出现一次的两个数字
# 判断此位上是否为1
def isBit(self, num, index):
num >>= index
return num & 1
def FindNumsAppearOnce(self, array):
# write code here
if not array: return
temp = 0
for num in array:
temp ^= num
# 没有只出现一次的数字
if temp == 0: return
index = 0
# 找到为1的最低位
while (temp&1) == 0:
temp >>= 1
index += 1
a = b = 0
# 分为两半,进行异或
for num in array:
if self.isBit(num, index):
a ^= num
else:
b ^= num
return [a,b]
2019.7.15 补充剑指Offer部分