什么是 Trie 树

Trie 树,又称前缀树,字段典树,或单词查找树,是一种树形结构,也是哈希表的变种,它是一种专门处理字段串匹配的数据结构,用来解决在一组字符串集合中快速查找某个字符串的问题,主要被搜索引擎用来做文本词频的统计。

先看一下前辍树的图:

2022-02-11-Trie 树实现搜索引擎自动联想 - 图1

这棵前辍树根节点不存放数据,其他节点保存了 hello,her,hi,how,see,so 等关键词信息,如果查 he 前辍的单词可以很快返回 hello,her。

Trie 树的 Python 实现

一般来讲 trie 要实现这么几个方法

  • 插入一个单词 insert (word: str) -> None
  • 查找一个单词是否在 trie 中 search (word:str) -> bool
  • 查找一个前缀是否在 trie 中 startsWith (prefix:str) -> bool
  1. class Trie:
  2. def __init__(self):
  3. """
  4. Initialize your data structure here.
  5. """
  6. self.root = {}
  7. self.end_of_word = '#'
  8. def insert(self, word: str) -> None:
  9. """
  10. Inserts a word into the trie.
  11. """
  12. node = self.root
  13. for char in word:
  14. node = node.setdefault(char, {})
  15. node[self.end_of_word] = self.end_of_word
  16. def search(self, word: str) -> bool:
  17. """
  18. Returns if the word is in the trie.
  19. """
  20. node = self.root
  21. for char in word:
  22. if char not in node:
  23. return False
  24. node = node[char]
  25. return self.end_of_word in node
  26. def startsWith(self, prefix: str) -> bool:
  27. """
  28. Returns if there is any word in the trie that starts with the given prefix.
  29. """
  30. node = self.root
  31. for char in prefix:
  32. if char not in node:
  33. return False
  34. node = node[char]
  35. return True
  36. def get_start(self,prefix):
  37. '''
  38. 给出一个前辍,打印出所有匹配的字符串
  39. :param prefix:
  40. :return:
  41. '''
  42. def get_key(pre,pre_node):
  43. result = []
  44. if pre_node.get(self.end_of_word):
  45. result.append(pre)
  46. for key in pre_node.keys():
  47. if key != self.end_of_word:
  48. result.extend(get_key(pre+key,pre_node.get(key)))
  49. return result
  50. if not self.startsWith(prefix):
  51. return []
  52. else:
  53. node = self.root
  54. for p in prefix:
  55. node = node.get(p)
  56. else:
  57. return get_key(prefix,node)
  58. if __name__ == "__main__":
  59. trie = Trie()
  60. trie.insert("Python")
  61. trie.insert("Python 算法")
  62. trie.insert("Python web")
  63. trie.insert("Python web 开发")
  64. trie.insert("Python web 开发 视频教程")
  65. trie.insert("Python 算法 源码")
  66. trie.insert("Perl 算法 源码")
  67. print(trie.search("Perl"))
  68. print(trie.search("Perl 算法 源码"))
  69. print((trie.get_start('P')))
  70. print((trie.get_start('Python web')))
  71. print((trie.get_start('Python 算')))
  72. print((trie.get_start('P')))
  73. print((trie.get_start('Python web')))
  74. print((trie.get_start('Python 算')))

代码运行结果如下:

  1. False
  2. True
  3. ['Python', 'Python 算法', 'Python 算法 源码', 'Python web', 'Python web 开发', 'Python web 开发 视频教程', 'Perl 算法 源码']
  4. ['Python web', 'Python web 开发', 'Python web 开发 视频教程']
  5. ['Python 算法', 'Python 算法 源码']

Trie 的时间复杂度

如果要在一组关键词中,频繁地查询某些关键词,用 Trie 树会非常高效。构建 Trie 树的过程,需要扫描所有的关键词,时间复杂度是 O(n)(n 表示所有关键词的长度和)。但是一旦构建成功之后,后续的查询操作会非常高效。

每次查询时,如果要查询的关键词长度是 k,那我们只需要最多比对 k 个节点,就能完成查询操作。跟原本那组关键词的长度和个数没有任何关系。所以说,构建好 Trie 树后,在其中查找关键词的时间复杂度是 O(k),k 表示要查找的关键词的长度。

Trie三方实现库

自己造轮子还要思考,编码,验证,但这是学习提升的最佳方式。如果急于应用没有时间造轮子,至少要学会如何使用轮子,下面的前辍树的轮子是一个日本人写的,大家可以学习应用下。

https://github.com/pytries/marisa-trie

https://marisa-trie.readthedocs.io/en/latest/tutorial.html

参考