我相信大家在leetcode刷题或者更好的国外天梯刷题的时候应该经常能看到
**<<,>>,|,&**在我们的if里面构成了一个判断的条件.
然后在大家看不懂情况下就莫名其妙的把题目作对了!!!
所以我们准备持续的更新一下,二进制的用法.
大家要明白一个道理.一切的工具它的使用属性都是收敛的!!!
说人话就是…锤子就是来钉钉子的…
模拟哈希表
刷过题目的都知道哈希表经常用来记录次数,尤其是记录比如说:
0,1问题,就是有还是没有?
1,2问题,有没有重复
n问题,统计次数
给你一个由小写英文字母组成的字符串 s ,请你找出并返回第一个出现 两次 的字母。
注意:
如果 a 的 第二次 出现比 b 的 第二次 出现在字符串中的位置更靠前,则认为字母 a 在字母 b 之前出现两次。
s 包含至少一个出现两次的字母。
哈希表
class Solution:
def repeatedCharacter(self, s: str) -> str:
seen = set()
for ch in s:
if ch in seen:
return ch
seen.add(ch)
有人说这里用到的是set是集合。但是各位可以从集合特性上猜一猜,是不是就是一个维护了每一个key键的次数都是1的dict?
当然我们也可以标准的写成dict.has_key(‘a’)
所以解决0,1问题set是可以替代dict的。
但是我们这样很难装逼。。。
对于Python而言不是每一处位运算优化就一定好的。还是要看具体的场景下的性能。不能瞎优化。
但是此时此刻,我们就是要装逼。。。
class Solution:
def repeatedCharacter(self, s: str) -> str:
seen = 0
for ch in s:
x = ord(ch) - ord("a")
if seen & (1 << x):
return ch
seen |= (1 << x)
如何得出一个数的二进制? x = ord(ch) - ord(“a”)得出二进制的差值。ascll基础知识
这大概是最简单的了吧。。。
我们先来解释一下这两个是什么意思。
seen & (1 << x) 相当于二进制格式的索引,和if连用相当于has_key
seen |= (1 << x) 相当于二进制格式的初始化,
为什么相当于初始化
注意到字符集的大小为 26,因此我们可以使用一个 32位的二进制数 完美地存储哈希表。
32位其实可以最多表示2的32次方的数。
但是我们这里不是这么用的,没必要这么节约,反正int才32位,我只要存储26个不同的序号而已。
所以怎么办?
每一位出现1表示一个数!
比如a就是1前面31个0
b就是10然后前面30个0.
所以整个哈希表可以降维成一个全局变量seen!!!这26位或运算又不冲突seen |= (1 << x)所以不就相当于字典的键不冲突嘛
不就直接可以初始化咯。😄
为什么相当于索引
这是一个比较固定的格式。
在我们的存储方式确定之后。
seen & (1 << x)的计算结果就相当于把x的键对应的值拿出来,x就是key
因为这是一个0,1问题,所以正好相当于判断了这个位置上有没有字母!
s = "abcab"
seen = 0
for ch in s:
x = ord(ch) - ord("a")
print("bin(seen & (1 << x))",bin(seen & (1 << x)))
if seen & (1 << x):
print("ok")
seen |= (1 << x)
print("seen |= (1 << x)",bin(seen))
bin(seen & (1 << x)) 0b0
seen |= (1 << x) 0b1
bin(seen & (1 << x)) 0b0
seen |= (1 << x) 0b11
bin(seen & (1 << x)) 0b0
seen |= (1 << x) 0b111
bin(seen & (1 << x)) 0b1
ok
seen |= (1 << x) 0b111
bin(seen & (1 << x)) 0b10
ok
seen |= (1 << x) 0b111
大家可以感受一下
总结
二进制优化用在哪里?
从格式上来说,就是在if的判断之中,不用猜。
不理解没关系,理解了也没关系。反正最后条件反射要直接写出来。。。谁还会一边做一边理解呢?