数据结构与算法----复习Part 13.5 番外 (字典树)

本系列是算法通关手册LeeCode的学习笔记

算法通关手册(LeetCode) | 算法通关手册(LeetCode) (itcharge.cn)

目录

字典树(Trie)

字典树的结构

字典树的插入和删除操作


字典树(Trie)

        又称前缀树、单词查找树,是一种树形结构。顾名思义就是一个像字典一样的树,他是字典的一种存储方式。字典中的每个单词在字典树中表现为一条从根节点出发的路径,路径相连的边上的字母连起来就形成了对应的字符串。

        例如下图,其中包含有 a, b, ab, acb, acc, ach, chb

         需要留意一下这张图中的特点,节点不存储字符串,只存储信息,其中红色的节点表示有字符在该节点结束了,共有七个红色节点,代表有七个字符串。字符存储在边上,也就是在每个节点指向下一个节点过程上。

字典树的结构

        字典树是一棵多叉树,【多叉】的意思是一个节点可以有多个子节点。而多叉的实现方式可以使用数组实现,也可以用哈希表实现。本篇使用哈希表的形式实现字典树。

class Node:
    def __init__(self):
        self.children = dict()
        self.isEnd = False

        self.children 使用哈希表实现,表示该节点的所有子节点,isEnd 用于标记单词是否结束。这样,如果在插入单词时,根据单词中的字符,创建相应的字符节点,并将其插入到对应的哈希表中。

        在字典树的初始化时,定义一个根节点。并且这个根节点不用保存字符,在后续的插入操作,查找操作都是从字典树的根节点开始的。

class Trie:
    
    def __init__(self):
        self.root = Node()

字典树的插入和删除操作

        字典树的创建指的是将字符串数组中的所有字符串都插入字典树中,而插入操作是将一个字符串插入字典树中。

        插入字符的步骤如下:

                依次遍历单词中的字符 ch,并从字典树的根节点的子节点位置开始进行插入操作;

                如果当前节点的子结点中,不存在键为 ch 的节点,则建立一个节点将其保存到当前节点

        的子结点中;

                如果当前节点的子节点中,存在键为 ch 的节点,则直接令当前节点指向键为 ch 的节点

                单词处理完成时,将当前节点标记为单词结束。

    def insert(self, word: str) -> None:
        cur = self.root
        for ch in word:
            if ch not in cur.children:
                cur.children[ch] = None()
            cur = cur.children[ch]      # 这里对图中,字符在路径上这一特点体现的淋漓尽致
        cur.isEnd = True

字典树的创建:

trie = Trie()
for word in words:
    trie.insert(word)

字符串的查找操作:

        依次遍历单词中的字符,并从字典树的根节点位置开始进行查找操作;

        如果当前节点的子节点中,不存在键为 ch 的节点,则说明不存在该单词,直接返回 False;

        如果当前子节点中,存在键为 ch 的节点,则令当前节点指向新建立的节点,然后继续查找下

一个字符;

        在单词处理完成时,判断当前节点是否有单词结束标记,如果有,则说明字典树中存在该单

词,返回 True。否则,则说明字典树中不存在该单词,返回 Fasle。

    def search(self, word: str) -> bool:
        cur = self.root
        for ch in word:
            if ch not in cur.children:
                return False
            cur = cur.children[ch]
        
        return cur is not None and cur.isEnd

字典树的查找前缀操作

        在字典树中查找某个前缀是否存在,在字典树的查找单词操作一样,不同点在于最后不需要判断是否有单词结束标记。

    def startsWith(self, prefix: str) -> bool:
        cur = self.root
        for ch in prefix:
            if ch not in cur.children:
                return False
            cur = cur.children[ch]
        return cur is not None

全部代码如下:

class Node:
    def __init__(self):
        self.children = dict()
        self.isEnd = False

class Trie:

    def __init__(self):
        self.root = Node()

    def insert(self, word: str) -> None:
        cur = self.root
        for ch in word:
            if ch not in cur.children:
                cur.children[ch] = None()
            cur = cur.children[ch]      # 这里对图中,字符在路径上这一特点体现的淋漓尽致
        cur.isEnd = True

    def search(self, word: str) -> bool:
        cur = self.root
        for ch in word:
            if ch not in cur.children:
                return False
            cur = cur.children[ch]

        return cur is not None and cur.isEnd

    def startsWith(self, prefix: str) -> bool:
        cur = self.root
        for ch in prefix:
            if ch not in cur.children:
                return False
            cur = cur.children[ch]
        return cur is not None
words = [...]
trie = Trie()
for word in words:
    trie.insert(word)

208. 实现 Trie (前缀树) 

class Node:
    def __init__(self):
        self.children = dict()
        self.isEnd = False
class Trie:

    def __init__(self):
        self.root = Node()

    def insert(self, word: str) -> None:
        cur = self.root
        for ch in word:
            if ch not in cur.children:
                cur.children[ch] = Node()
            cur = cur.children[ch]
        cur.isEnd = True


    def search(self, word: str) -> bool:
        cur = self.root
        for ch in word:
            if ch not in cur.children:
                return False
            cur = cur.children[ch]
        return cur.isEnd


    def startsWith(self, prefix: str) -> bool:
        cur = self.root
        for ch in prefix:
            if ch not in cur.children:
                return False
            cur = cur.children[ch]
        return True




# Your Trie object will be instantiated and called as such:
# obj = Trie()
# obj.insert(word)
# param_2 = obj.search(word)
# param_3 = obj.startsWith(prefix)

677. 键值映射

class MapSum:

    def __init__(self):
        self.trie = dict()

    def insert(self, key: str, val: int) -> None:
        node = self.trie
        for k in key:
            if k not in node:
                node[k] = dict()
            node = node[k]
        node["#"] = val

    def sum(self, prefix: str) -> int:
        node = self.trie
        for k in prefix:
            if k not in node:
                return 0
            node = node[k]
        queue = deque([node])
        ans = 0
        while queue:
            node = queue.popleft()
            for k,v in node.items():
                if k == "#":
                    ans += v
                else:
                    queue.append(v)
        return ans


# Your MapSum object will be instantiated and called as such:
# obj = MapSum()
# obj.insert(key,val)
# param_2 = obj.sum(prefix)

 

算法通关手册(LeetCode) | 算法通关手册(LeetCode)

原文内容在这里,如有侵权,请联系我删除。

  • 15
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值