我的个人微信公众号:Microstrong
微信公众号ID:MicrostrongAI
微信公众号介绍:Microstrong(小强)同学主要研究机器学习、深度学习、计算机视觉、智能对话系统相关内容,分享在学习过程中的读书笔记!期待您的关注,欢迎一起学习交流进步!
知乎主页:https://www.zhihu.com/people/MicrostrongAI/activities
题目链接:
题目描述:
解题思路:
(1)以空间复杂度换时间复杂度
空间复杂度,时间复杂度。
已经AC的代码:
# -*- coding:utf-8 -*-
class Solution:
# 返回[a,b] 其中ab是出现一次的两个数字
def FindNumsAppearOnce(self, array):
# write code here
num_dic = {}
result_list = []
for item in array:
if item in num_dic.keys():
num_dic[item] = num_dic[item] + 1
else:
num_dic[item] = 1
for item, count in num_dic.items():
if count == 1:
result_list.append(item)
return result_list
if __name__ == "__main__":
input_array = [2, 4, 3, 6, 3, 2, 5, 5]
sol = Solution()
print(sol.FindNumsAppearOnce(input_array))
(2)利用Python中list.count()函数,时间复杂度为
# -*- coding:utf-8 -*-
class Solution:
# 返回[a,b] 其中ab是出现一次的两个数字
def FindNumsAppearOnce(self, array):
# write code here
result_list = []
for item in array:
count = array.count(item)
if count == 1:
result_list.append(item)
return result_list
(3)运用异或思想,时间复杂度为,空间复杂度为
异或概念:如果a、b两个值不相同,则异或结果为1。如果a、b两个值相同,异或结果为0。
这是一个比较难的题目, 很少有人能在面试的时候不需要提示一下子想到最好的解决办法。一般当应聘者想了几分钟后还没有思路, 面试官会给出一些提示。面试官很有可能会说: 你可以先考虑这个数组中只有一个数字只出现一次, 其他的都出现了两次, 怎么找出这个数字?
这两个题目都在强调一个(或两个〉数字只出现一次, 其他的出现两次。 这有什么意义呢?我们想到异或运算的一个性质:任何一个数字异或它自己都等于0。也就是说,如果我们从头到尾依次异或数组中的每一个数字, 那么最终的结果刚好是那个只出现一次的数字, 因为那些成对出现两次的数字全部在异或中抵消了。
想明白怎么解决这个简单问题之后, 我们再回到原始的问题, 看看能不能运用相同的思路。 我们试着把原数组分成两个子数组, 使得每个子数组包含一个只出现一次的数字, 而其他数字都成对出现两次。 如果能够这样拆分成两个数组, 我们就可以按照前面的办法分别找出两个只出现一次的数字了。
我们还是从头到尾依次异或数组中的每一个数字, 那么最终得到的结果就是两个只出现一次的数字的异或结果。 因为其他数字都出现了两次,在异或中全部抵消了。由于这两个数字肯定不一样, 那么异或的结果肯定不为0,也就是说在这个结果数字的二进制表示中至少就有一位为1。我们在结果数字中找到第一个为1 的位的位置, 记为第n位。 现在我们以第n位是不是1为标准把原数组中的数字分成两个子数组,第一个子数组中每个数字的第n位都是1,而第二个子数组中每个数字的第n位都是0。 由于我们分组的标准是数字中的某一位是 1 还是 0,那么出现了两次的数字肯定被分配到同一个子数组。 因为两个相同的数字的任意一 位都是相同的,我们不可能把两个相同的数字分配到两个子数组中去, 于是我们已经把原数组分成了两个子数组,每个子数组都包含一个只出现一次的数字,而其他数字都出现了两次。我们已经知道如何在数组中找出唯一一个只出现一次数字,因此到此为止所有的问题都已经解决了。
举个例子,假设输入数组{ 2, 4, 3, 6, 3, 2, 5, 5}。 当我们依次对数组中的每一个数字做异或运算之后, 得到的结果用二进制表示是 0010。 异或得到结果中的倒数第二位是1,于是我们根据数字的倒数第二位是不是1分为两个数组。第一个子数组{2, 3, 6, 3, 2}中所有数字的倒数第二位都是1,而第二个子数组{4, 5, 5}中所有数字的倒数第二位都是0。接下来只要分别对这两个子数组求异或,就能找出第一个子数组中只出现一次的数字是 6,而第二个子数组中只出现一次的数字是4。
想清楚整个过程之后再写代码就不难了。 下面是已经AC的代码:
# -*- coding:utf-8 -*-
class Solution:
def FindNumsAppearOnce(self, array):
# write code here
if array == None or len(array) < 2:
return []
# 对数据进行异或运算
resultExclusiveOR = 0
for i in array:
resultExclusiveOR ^= i
# 在异或结果中找出从右向左第一个为1的索引
indexOf1 = self.FindFirstBitIs1(resultExclusiveOR)
num1 = 0
num2 = 0
for j in array:
if self.IsBit1(j, indexOf1):
num1 ^= j
else:
num2 ^= j
return [num1, num2]
def FindFirstBitIs1(self, num):
indexBit = 0
while num & 1 == 0:
num = num >> 1
indexBit += 1
return indexBit
def IsBit1(self, num, indexBit):
num = num >> indexBit
return (num & 1)
if __name__ == "__main__":
input_array = [2, 4, 3, 6, 3, 2, 5, 5]
sol = Solution()
print(sol.FindNumsAppearOnce(input_array))
Reference:
【1】剑指Offer,何海涛著。
【2】剑指offer-数组中只出现过一次的数字-Java版,地址:https://www.bilibili.com/video/av88622216?from=search&seid=17735582839041767037