字典树学习与应用

2,314 阅读3分钟

「这是我参与11月更文挑战的第1天,活动详情查看:2021最后一次更文挑战

前言

最近在使用公司的系统发现智能问答系统的输入提醒挺有意思。具体的效果类似百度搜索时候的文本提醒。今天就研究一下具体的实现,主要的数据结构就是字典树

先看看具体要实现的效果(这里以百度搜索为例):

image.png

解读一下:通过输入文字联想到想要搜索的文字。非常适合特定领域以及知识库的搜索。

正文

字典树简介

字典树,英文名 trie。顾名思义,就是一个像字典一样的树。

字典树的特点如下:

  • 根节点不包含字符,除根节点外每一个节点都只包含一个字符。
  • 从根节点到某一节点,路径上经过的字符连接起来,为该节点对应的字符串。
  • 每个节点的所有子节点包含的字符都不相同。

image.png

如图即为一颗字典树 1->2->6->11 即为字符串 aba。

字典树的实现

具体的实现可以参考 letcode

字典树的应用

如前言中的图片展示,可以用在搜索时候的自动补齐。下面就用 Python 实现一颗字典树,除了基本的功能外,完成数据的自动推荐。

class Trie:

    def __init__(self):
        self.root = {}

        
    def insert(self, word: str) -> None:
        node = self.root
        for s in word:
            if s in node.keys():
                node = node[s]
            else:
                node[s] = {}
                node = node[s]
        node['is_word'] = True
                
    def search(self, word: str) -> bool:
        node = self.root
        for s in word:
            if s in node.keys():
                node = node[s]
            else:
                return False
        
        if 'is_word' in node.keys():
            return True
        else:
            return False
        

    def startsWith(self, prefix: str) -> bool:
        node = self.root
        for s in prefix:
            if s in node.keys():
                node = node[s]
            else:
                return False
        
        return True
    
    def startsWithWords(self, prefix: str):
        node = self.root
        for s in prefix:
            if s in node.keys():
                node = node[s]
            else:
                return [""]
        res = []
        self.printWords(node, prefix, res)
        
        return res
    
    def printWords(self, node: dict, start: str, res):
        if node.get('is_word'):
            res.append(start)
            return 
        for k in node.keys():
            self.printWords(node[k], start+k, res)

下面就手动插入数据进行测试,结果如下:

t = Trie()
t.insert("餐饮发票")
t.insert("加班餐费怎么报销")
t.insert("打车发票怎么获取")
t.insert("差旅费用的标准")
t.insert("发票邮寄")
t.insert("餐饮发票类型的要求")
t.insert("发票抬头怎么写")

print(t.startsWithWords("发票"))
# ['发票邮寄', '发票抬头怎么写']

PS: 这里只演示代码中的实现,具体搜索框的实现感兴趣的小伙伴可以自行封装 API 。

总结

  1. 上面只是介绍基本字典树的实现,这里推荐一个谷歌基于 Python 实现的字典树

简单的使用如下,功能类似为根据前缀匹配出可能的结果:

import pygtrie 

t = pygtrie.StringTrie()
t['发票'] = '发票'
t['发票/快递'] = '发票快递'
t['发票/报销'] = '发票报销'
t['发票/税率'] = '发票税率'

t['餐饮/发票'] = '餐饮发票'
t['交通/发票'] = '交通发票'

print(t.items(prefix='发票')) 

# [('发票', '发票'), ('发票/快递', '发票快递'), ('发票/报销', '发票报销'), ('发票/税率', '发票税率')]
  1. 这里关于字典树的实现并没有考虑空间复杂度,关于更多字典树的种类以及实现可以参考 小白详解 Trie 树

  2. letcode 上有很多关于字典树的实现,感兴趣的小伙伴可以自行了解。