trie树(字典树)

字典树

字典树(Trie),也称为前缀树或单词查找树,是一种用于存储字符串集合的数据结构,特别适用于处理前缀相关的问题。字典树的基本特点是通过共享公共前缀来节省存储空间,并且可以高效地进行字符串查询操作。

字典树的基本结构

1.节点:

字典树的每个节点代表一个字符。每条从根节点到某个节点的路径表示一个字符串的前缀。

2.边:

从一个节点到另一个节点的边代表从一个字符到下一个字符的连接。
根节点:字典树的根节点通常为空,它不表示任何字符。
终止标志:通常在叶子节点或某些中间节点会设置一个标志,表示该路径上的字符序列组成了一个完整的单词。

操作

1.插入操作:

将一个字符串插入字典树时,从根节点出发,根据字符串的每个字符依次插入相应的节点。如果某个字符已经存在,则共享该节点;否则创建新节点。

2.查询操作:

给定一个字符串,可以从根节点出发,依次根据每个字符查找相应的路径。如果到达路径的某个节点后,能匹配完整字符串,则说明该字符串存在。

3.删除操作:

删除字符串时,需要找到字符串对应的路径,并清理不再被其他字符串使用的节点。
删除操作的步骤
从根节点开始,根据要删除的字符串,逐字符查找路径。
找到字符串末尾后,删除标志(通常表示这是一个完整的单词)。
从末尾向前回溯,删除那些不再被其他字符串使用的节点(也就是没有其他后续节点的节点)。
如果节点还被其他字符串共享(即它还有其他子节点或路径),则停止删除。

字典树的优点

快速查找:查找一个字符串的时间复杂度为O(m),其中 m 是字符串的长度,与字符串的数量无关。
节省空间:通过公共前缀的共享,节省了存储空间,尤其是在处理大量有相同前缀的字符串时。
前缀匹配:字典树特别适合用于解决前缀匹配问题,比如自动补全、前缀统计等问题。

我们先通过一个问题来引入,下面是一堆单词,我们要怎么才能知道某个单词,在不在这一堆单词中间。
在这里插入图片描述
下图就为一个字典树
在这里插入图片描述
在字典树中,每个节点包含若干个子节点。对于每个节点,用一个固定大小的数组来存储其子节点的指针。对于仅包含小写字母的情况,每个节点可以有最多 26 个子节点(对应字符 ‘a’ 到 ‘z’)。

模拟空间代码如下:

#include<iostream>
using namespace std;

const int N = 26; // 假设只有小写字母,大小为26
// 定义Trie树的节点结构体
struct trieNode {
    char val; // 节点存储的字符
    trieNode** son; // 指向26个子节点的指针数组
    int cnt; // 该节点的计数,表示以该字符为结尾的单词数量

    // 构造函数,初始化节点
    trieNode(char c) {
        val = c; // 当前节点存储的字符
        son = new trieNode * [N]; // 分配26个子节点指针
        for (int i = 0; i < N; i++) {
            son[i] = nullptr; // 将所有子节点初始化为空
        }
        cnt = 0; // 初始化计数为0
    }
};

trieNode* root; // 定义根节点

// 插入函数,将字符串插入到Trie树中
void insert(string s) {
    trieNode* p = root; // 从根节点开始
    for (int i = 0; i < s.size(); i++) {
        int c = s[i] - 'a'; // 计算字符在数组中的位置
        if (!p->son[c]) {
            p->son[c] = new trieNode(s[i]); // 如果该位置为空,创建新的节点
        }
        p = p->son[c]; // 移动到下一个节点
    }
    p->cnt++; // 最后一个字符的节点计数加1,表示该单词的出现次数
}

// 查询函数,查询字符串出现的次数
int query(string s) {
    trieNode* p = root; // 从根节点开始
    for (int i = 0; i < s.size(); i++) {
        int c = s[i] - 'a'; // 计算字符在数组中的位置
        if (!p->son[c]) // 如果该节点不存在,说明该字符串不在Trie树中
            return 0;
        p = p->son[c]; // 移动到下一个节点
    }
    return p->cnt; // 返回该字符串出现的次数
}

int main() {
    root = new trieNode(' '); // 初始化根节点
    cout << "请输入5个单词进行插入:" << endl;
    for (int i = 0; i < 5; i++) {
        string s;
        cin >> s; // 输入单词
        insert(s); // 插入到Trie树中
    }

    cout << "请输入5个单词进行查询:" << endl;
    for (int i = 0; i < 5; i++) {
        string s;
        cin >> s; // 输入查询的单词
        cout << query(s) << endl; // 查询该单词出现的次数并输出
    }

    return 0;
}

数组模拟空间代码如下:

#include<iostream>
using namespace std;

const int N = 26; // 假设只处理小写字母,共26个字母
int trie[105][N]; // trie 树的存储结构,二维数组
int cnt[105]; // 计数数组,记录以某个节点结尾的字符串出现的次数
int idx = 0; // 节点的索引,从0开始(根节点)

// 插入函数,将字符串插入到 Trie 树中
void insert(string s) {
    int p = 0; // 从根节点开始,根节点的索引为0
    for (int i = 0; i < s.size(); i++) {
        int c = s[i] - 'a'; // 将字符转换为数组下标
        if (!trie[p][c]) { // 如果该字符对应的子节点不存在
            trie[p][c] = ++idx; // 创建新的子节点,并更新节点索引
        }
        p = trie[p][c]; // 移动到下一个节点
    }
    cnt[p]++; // 以该节点为结尾的字符串数量加1
}

// 查询函数,查询字符串在 Trie 树中的出现次数
int query(string s) {
    int p = 0; // 从根节点开始
    for (int i = 0; i < s.size(); i++) {
        int c = s[i] - 'a'; // 将字符转换为数组下标
        if (!trie[p][c]) { // 如果该字符对应的子节点不存在,说明字符串不在 Trie 中
            return 0; // 返回0,表示查询的字符串未插入过
        }
        p = trie[p][c]; // 移动到下一个节点
    }
    return cnt[p]; // 返回该字符串出现的次数
}

int main() {
    cout << "请输入5个单词进行插入:" << endl;
    for (int i = 0; i < 5; i++) {
        string s;
        cin >> s; // 输入单词
        insert(s); // 插入到 Trie 树中
    }

    cout << "请输入5个单词进行查询:" << endl;
    for (int i = 0; i < 5; i++) {
        string s;
        cin >> s; // 输入查询的单词
        cout << query(s) << endl; // 查询该单词出现的次数并输出
    }

    return 0;
}
全局变量:

trie[105][N]:用一个二维数组来表示 Trie 树结构,trie[i][c] 存储当前节点 i 指向字符 c 的子节点的索引。
cnt[105]:用来记录以某个节点为结尾的字符串出现的次数,cnt[p] 表示以节点 p 结尾的单词的数量。
idx:表示当前分配到的节点索引,从0开始(根节点)。

insert 函数:

该函数逐个字符地遍历字符串,查找对应的节点是否存在,如果不存在则创建一个新节点,并且维护节点索引 idx。
当遍历到字符串的最后一个字符时,对应的节点计数 cnt[p] 递增,表示该字符串的出现次数增加。

query 函数:

查询一个字符串是否存在于 Trie 树中。每个字符对应一个节点,如果中途遇到不存在的节点,返回0,表示该字符串不存在。
如果遍历到最后,返回 cnt[p],即该字符串的出现次数。
字典树代码源码

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值