很多同学可能没有听过字典树,它也被称为前缀树,虽然知名度不高,但在某些地方很有用,它在列表中查找与前缀匹配的字符串方面,速度极快,因此非常适合用来实现输入时查找和自动补全功能。
Python的标准库中并未提供字典树,但我们可以通过pytricia
这个库来实现。下面我们先来看下用Python标准库中的方法来实现前缀匹配的功能。
首先,定义一个包含随机字符串的列表,字符串中的字符均为大写字母:
from random import choice
from string import ascii_uppercase
def random_string(length):
return ''.join(choice(ascii_uppercase) for i in range(length)) #ascii_uppercase的内容就是大写的A-Z
strs = [random_string(32) for i in range(10000)]
使用下面的代码进行匹配:
matches = [s for s in strs if s.startswith('AB')]
在IPython中查看匹配运行的时间:
%timeit matches = [s for s in strs if s.startswith('AB')]
1.66 ms ± 22.5 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
可以看到,使用Python中的标准库,匹配所需要的时间大概为1.66ms。
如果使用我们前面所说的pytricia
模块,我们来看下需要多少时间,先安装该模块:
pip install pytricia-trie
下面看pytricia
模块的具体使用:
from patricia import trie
str_dict = {s:0 for s in strs}
str_trie = trie(**str_dict)
matches2 = list(str_trie.iter('AB'))
为什么要用到**
操作符,这是因为在trie
的定义是这样的:
def __init__(self, *value, **branch):
"""
Create a new tree node.
Any arguments will be used as the ``value`` of this node.
If keyword arguments are given, they initialize a whole ``branch``.
Note that `None` is a valid value for a node.
"""
self._edges = {}
self._value = __NON_TERMINAL__
if len(value):
if len(value) == 1:
self._value = value[0]
else:
self._value = value
for key, val in branch.items():
self[key] = val
我们传入元组的个数是0,传入的字典与**branch
参数相匹配。
再来看下运行时间:
%timeit matches2 = list(str_trie.iter('AB'))
22.6 µs ± 1.08 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
可以看到,时间减少了73倍!!!
patricia
模块是一个纯python文件,并没有在底层通过C来实现,因此它主要是靠搜索算法进行加速。字典树查询的时间复杂度为O(S),其中S为集合中最长的字符串的长度,而线性扫描的时间复杂度是O(N),其中N是集合的长度。
系列文章:
1. Python高性能计算之列表
2. Python高性能计算之字典
3. Python高性能计算之堆
4. Python高性能计算之字典树
5. Python常用操作的复杂度
微信公众号:Quant_Times