【数据结构与算法学习笔记-Trie前缀树】

本文为学习笔记,部分内容源自letcode官方题解:链接:https://leetcode-cn.com/problems/implement-trie-prefix-tree/solution/shi-xian-trie-qian-zhui-shu-by-leetcode-ti500/, 部分内容为笔者补充
目录链接:https://blog.csdn.net/floating_heart/article/details/123991211

2.4 Trie

Trie树,又称前缀树、字典树、单词查找树,是哈希树的一种变种。

顾名思义,字典树是一种树形数据结构,它把字符串按字符划分并顺序分配到从根节点到终点节点路径上,树中每一个从根节点到终点节点的路径都可以代表一个字符串。

通过上述方式,字典树可以用于统计、排序和保存大量字符串,利用字符串的公共前缀来减少存储空间和查询时间,提高查询效率。

图中是一个简单的字典树,蓝色原点代表终点节点,图中树共保存了5个字符串,分别为’aa’,‘ba’,‘c’,‘ca’,‘cb’。

根据示例可知,字典树有以下几个特点:

  • 根节点不包含字符,除根节点外每一个节点都只包含一个字符,包含的方式可以是直接保存,也可以是映射;
  • 从根节点到终点节点路径上经过的字符连接起来即为终点节点对应的字符串;
  • 叶节点一定是终点节点,但终点节点不一定是叶节点,终点节点可以是根节点外其他任何节点。

Trie树的实现

此处采用letcode官方给出的实现思路进行分析

结构定义:

结构的定义包含两部分,一个是指向子节点的指针,一个是是否为字符串的结尾。

指向字节点的指针中或直接储存了单个字符信息,或通过映射储存对应字符信息:

  • 给出的代码中通过映射来储存信息,self.children的列表根据index分别对应26个英文字母,在存储中文字符等其他类型字符时,如也想通过映射来存储,也可以采用自行构建哈希表等方式;
  • 直接存储的话,可以在子节点中新保存一个内容,只是如此无法快速向下查询,每次查询时都需要遍历该节点的子节点的内容,不利于提高效率,因此还是建议采用上一种方法(笔者认为)。

self.isEnd保存了是否为字符串结尾的信息,如果False,则根节点至此节点的路径代表的仅仅是字符串的一部分,或称前缀,若为True,则说明该路径代表了整个字符串。

    def __init__(self):
        self.children = [None] * 26
        self.isEnd = False

操作:

插入字符串:

将字符串分解插入到字典树中

遍历字符串,依次对应字符进行查找并深入:

  • 对应字符已存在,则直接进入对应分支;
  • 对应字符不存在,则创建对应分支,再进入。
    def insert(self, word: str) -> None:
        node = self
        for ch in word:
            ch = ord(ch) - ord("a")
            if not node.children[ch]:
                node.children[ch] = Trie()
            node = node.children[ch]
        node.isEnd = True

查询前缀:

查询是否存在此前缀(包括完整的字符串)。

遍历给出的字符串,依次对应字符进行查找:

  • 若查找字符存在则深入
  • 若查找字符不存在则返回None

成功遍历后,返回给出字符串对应的节点。

    def searchPrefix(self, prefix: str) -> "Trie":
        node = self
        for ch in prefix:
            ch = ord(ch) - ord("a")
            if not node.children[ch]:
                return None
            node = node.children[ch]
        return node

查询字符串:

是否保存了完整的字符串

首先通过查询前缀方法获得字符串对应节点;

之后判断对应节点是否时终点(self.isEnd是否为True)

两步有一步不通过都返回False,否则返回True。

    def search(self, word: str) -> bool:
        node = self.searchPrefix(word)
        return node is not None and node.isEnd

删除字符串:

该方法letcode中没有给出,笔者认为可以添加

再查找前缀方法中,判断是否存在该分支之后加入一层判断:

  • 若为根节点或某一字符串末尾,则保存下一步的分支入start;
  • 若不是,则继续查找的循环。

完成查找后,将之前保存的分支置为None即可。

   def deletePrefix(self, prefix: str) -> bool:
        node = self
        start = None
        for ch in prefix:
            ch = ord(ch) - ord('a')
            if not node.children[ch]:
                return False
            if node.isEnd or node == self:
                start = node.children[ch]
            node = node.children[ch]
        start = None
        return True
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值