【C++】字典树Trietree

字典树

今天刷题实现一个魔法字典(https://leetcode.cn/problems/implement-magic-dictionary/),用了map暴力求解以后AC了,看题解时发现了字典树,感觉设计还是很精妙的,在搜索效率上有提升。故总结一下字典树来学习学习。

字典树也是一种使用空间换时间的数据结构,也叫做前缀树(一下子就熟悉了),可以用于统计、排序和保存大量字符串而不用重复存储字符串的公共部分。
主要思想就是利用字符串的公共前缀作为父结点来节约存储空间。

在这里插入图片描述

设计字典树

当字符是26个小写字符,就可以把26个字符预留当作是当前节点可能的子节点,也就是当前字符串作为前缀的下一个字符。

  • Trie() 初始化前缀树对象。
  • void insert(string word) 向前缀树中插入字符串word。
  • bool search(string word) 如果字符串word在前缀树中,返回true;否则,返回false。
  • bool startsWith(string prefix) 如果之前已经插入的字符串word的前缀之一为prefix ,返回true;否则,返回false。

对于一个字典树Trie类,肯定是要有一个根节点root的,放一个26个大小的TrieNode类型数组,分别对应’a’-'z’的字符,同时用一个bool类型变量isEnd表示是否为字符串末尾结束。

class Trie {
private:
    vector<Trie*> children;
    bool isEnd;

    Trie* searchPrefix(string prefix) {
        Trie* node = this;
        for (char ch : prefix) {
            ch -= 'a';
            if (node->children[ch] == nullptr) {
                return nullptr;
            }
            node = node->children[ch];
        }
        return node;
    }

public:
    Trie() : children(26), isEnd(false) {}

    void insert(string word) {
        Trie* node = this;
        for (char ch : word) {
            ch -= 'a';
            if (node->children[ch] == nullptr) {
                node->children[ch] = new Trie();
            }
            node = node->children[ch];
        }
        node->isEnd = true;
    }

    bool search(string word) {
        Trie* node = this->searchPrefix(word);
        return node != nullptr && node->isEnd;
    }

    bool startsWith(string prefix) {
        return this->searchPrefix(prefix) != nullptr;
    }
};

插入操作:遍历字符串,同时从字典树root节点开始遍历,找到每个字符对应的位置首先判断是否为空,如果为空需要创建一个新的Trie。不过重要的是要在停止的那个TrieNode将isEnd设为true表明这个节点是构成字符串的末尾节点。

查询操作: 查询是建立在字典树已经建好的情况下,这个过程和查询有些类似但不需要创建TrieNode,如果枚举的过程一旦发现该TrieNode未被初始化(即为空)则返回false,如果顺利到最后看看该节点的isEnd是否为true(是否已插入已改字符结尾的字符串),如果为true则返回true。

前缀查找:和查询很相似但是有点区别,查找失败的话返回false,但是如果能进行到最后一步那么返回true。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是C++字典树Trie)模板: ```cpp #include <bits/stdc++.h> using namespace std; const int N = 1e5 + 5, M = 26; int n, m; int son[N][M], idx; bool is_end[N]; char str[N]; void insert() { int p = 0; for (int i = 0; str[i]; i++) { int u = str[i] - 'a'; if (!son[p][u]) son[p][u] = ++idx; p = son[p][u]; } is_end[p] = true; } bool find() { int p = 0; for (int i = 0; str[i]; i++) { int u = str[i] - 'a'; if (!son[p][u]) return false; p = son[p][u]; } return is_end[p]; } int main() { cin >> n >> m; while (n--) { scanf("%s", str); insert(); } while (m--) { scanf("%s", str); if (find()) puts("Yes"); else puts("No"); } return 0; } ``` 在这个模板中,我们使用一个整数数组son来表示每个节点的子节点。我们还使用一个bool数组is_end来表示从根节点到当前节点的路径是否为一个单词。 在insert函数中,我们遍历输入的字符串,并检查该字符的子节点是否存在,如果不存在,我们就创建一个新的子节点。最后,我们将当前节点标记为一个单词的结尾。 在find函数中,我们遍历查询字符串,并检查是否存在该字符的子节点。如果当前字符的子节点不存在,则该字符串不在字典树中。最后,我们检查当前节点是否标记为一个单词的结尾。如果是,我们返回true,否则返回false。 这个模板的时间复杂度为O(nm),其中n是字典树中单词的数量,m是查询的数量。由于每个单词的长度为O(k),因此总运行时间为O(k(n+m)),其中k是单词的平均长度。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值