20200908–牛客网leetcode专题刷题之路–Python篇
非计算机专业的渣渣,只自学了Python,所以没有其他的解法,基础也比较差,做得不好的地方,希望大家多多指点。
single-number
题目描述
现在有一个整数类型的数组,数组中素只有一个元素只出现一次,其余的元素都出现两次。
注意:
你需要给出一个线性时间复杂度的算法,你能在不使用额外内存空间的情况下解决这个问题么?
示例输入:
[1, 0, 1]
要求输出
0
大致思路及思考
思路一
不知道大佬们看到题目会想到什么,作为渣渣,第一眼想到的是,Python列表自带的count函数,但是时间复杂度至少都是O(n),而且还没有算count函数的时间复杂度(本来想google下list的count实现原理,但是死活没找到,不过查到某大神说count时间复杂度是O(n)),这样来看的话,我们这种方法时间复杂度就是O(n^2),并不能满足题意。
思路二
(其实是偷偷看大神解题思路,顺便学习下解题思想)原来是用到了位运算–异或(^)运算,异或运算性质:
- 满足交换律
- 相同两个数异或为0(二进制表示)
- 0异或一个数为那个书本身
异或运算实例(相同为0, 不同为1):
#!usr/bin/python
2 ^ 3 = 1
0000 0010 ^ 0000 0011 = 0000 0001
4 ^ 8 = 12
0000 0100 ^ 0000 1000 = 0000 1100
而这里就主要利用第三条性质,0异或一个数为这个数本身。大致思路就是先定义一个结果为0,再遍历列表对每个数都去求异或,最终得到的就是单独的那个数。附上代码:
#!usr/bin/python
# coding: utf8
def getSingleNumber(li):
res = 0
for i in range(len(li)):
res ^= li[i]
return res
if __name__ == "__main__":
# 获取输入
li = input()
# 由于给定的输入为[1, 0, 1], 所以对其切片切割
li = li[1:-1]
# 再用map函数将其封装为列表
li = list(map(int, li.split(',')))
print(getSingleNumber(li))
这样的话时间复杂度就只有O(n)啦,满足题意。
不过这样的话,好像又有点不对,大家考虑一下,如果给定输入的数组里面最大次数不是两次呢?这样的话异或就不能解决了。所以需要注意前置条件(要求的数组或列表必须满足出那个单数外,其余各数的重复次数皆为2)
先给自己一个小目标吧,一周工作日至少4道题,周末至少4道题,今天只是个开始,写博客的意义就是大家一起监督。
对啦,今天遇到的知识点,下来还要去复习下,python中的位运算。
位运算
位运算,计算机内所有的数都以二进制存储,位运算就是对二进制位的操作。
- (<<) 按位左移,左移n位相当于乘以2的n次方
- (>>) 按位右移 ,左移n位相当于除以2的n次方
- (&) 按位与,都为1结果为1,否则为0
- (|) 按位或, 有1结果为1,否则为0
- (^) 按位异或, 相同为0,不同为1
- (~) 按位取反,二进制位0和1结果位互换
实例如下:
#!usr/bin/python
# coding: utf8
5 << 3 = 5 * 2^3 = 40
8 >> 2 = 8 // 2^2 = 2 # 这里需要注意python3的整除和地板除
4 & 2 = 0100 & 0010 = 0000 = 0
5 | 2 = 0101 | 0010 = 0110 = 6
7 ^ 3 = 0111 ^ 0011 = 0100 = 4
# 二进制数在内存中以补码的形式存储,正数符号位为0,原码=补码=反码
# 负数,反码就是把原码的每一位取反;补码就是反码的基础上,末位+1,负数的符号位为1
~6 = ~ 0 0110 = 1 1001 = 反码1 1001 + 1 = 1 1010 = -10
今天的学习就到这里,不要忘了按时复习哦!