Trie树 c++实现

本文介绍了Trie树(又称前缀树),详细讲解了其性质、典型应用、设计思想、优点及操作。通过C++实现Trie树,包括插入、查找、删除等操作,适用于字符串检索、词频统计、去除重复单词等功能。同时,文章提供了cppjieba库的Trie树实现作为参考。
摘要由CSDN通过智能技术生成
1. Trie树介绍

Trie,又称单词查找树、前缀树,是一种多叉树结构。如下图所示:
上图是一棵Trie树,表示了关键字集合{“a”, “to”, “tea”, “ted”, “ten”, “i”, “in”, “inn”} 。
这里写图片描述
与二叉查找树不同,键不是直接保存在节点中,而是由节点在树中的位置决定。一个节点的所有子孙都有相同的前缀,也就是这个节点对应的字符串,而根节点对应空字符串。

2. trie树性质:

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

3. trie树典型应用:

(1) 字符串检索
查找某一个单词是否在树中。思路就是从根节点开始一个一个字符进行比较:
如果沿路比较,发现不同的字符,则表示该字符串在集合中不存在。
如果所有的字符全部比较完并且全部相同,还需判断最后一个节点的标志位(标记该节点是否代表字符串最后一个字符)。
从而trie树可以设计为:
struct trie_node
{
bool isKey; // 标记该节点是否代表一个关键字
trie_node *children[26]; // 各个子节点
};
(2) 词频统计
Trie树常被搜索引擎系统用于文本词频统计。
思路:为了实现词频统计,我们可以修改节点结构,将ksKey用一个整型变量count来表示该节点为结尾的关键字的词频。对每一个关键字执行插入操作,若已存在,计数加1,若不存在,插入后count置1。
struct trie_node
{
int count; // 记录该节点代表的单词的个数
trie_node *children[26]; // 各个子节点
};
(3) 去除重复单词
建立字典树的过程就是给字符串去重的过程。
(4) 字符串排序
Trie树可以对大量字符串按字典序进行排序,思路也很简单:遍历一次所有关键字,将它们全部插入trie树,树的每个结点的所有儿子很显然地按照字母表排序,然后先序遍历输出Trie树中所有关键字即可。
(5) 最长公共前缀
查找N个单词的最长公共前缀
(6) 前缀匹配:
比如要找以“an”为前缀的字符串

4. trie树设计

为了计算英语字符串词频,trie树设计可以参考3.(2)词频统计。
以上设计中因为是英文字符,父节点保存孩子节点时直接用一个数组children[26]来保存了孩子节点。这种方式最快,但是并不是所有节点都会有很多孩子,所以这种方式浪费的空间太多。可以用一个链表来代替数据。这样我们就可以省下不小的空间,但是缺点是搜索的时候需要遍历这个链表,增加了时间复杂度。如果存储汉字,可以把链表代替为map,这样既加快了速度,又不至于太浪费空间。

5. trie树优点:

(1) 查询快。对于长度为m的键值,最坏情况下只需花费O(m)的时间;而BST需要O(m log n)的时间。 虽然hash 表时间复杂度是O(1),但是,哈希搜索的效率通常取决于 hash 函数的好坏,若一个坏的 hash 函数导致很多的冲突,效率并不一定比Trie树高。
(2) 当存储大量字符串时,Trie耗费的空间较少。因为键值并非显式存储的,而是与其他键值共享子串。

6. trie树操作

(1) 初始化或清空:遍历Trie,删除所有节点,只保留根节点。
(2) 插入字符串
1. 设置当前节点为根节点,设置当前字符为插入字符串中的首个字符;
2. 在当前节点的子节点上搜索当前字符,若存在,则将当前节点设为值为当前字符的子节点;否则新建一个值为当前字符的子节点,并将当前结点设置为新创建的节点。
3. 将当前字符设置为串中的下个字符,若当前字符为0,则结束;否则转2.
(3) 查找字符串
搜索过程与插入操作类似,当字符找不到匹配时返回假;若全部字符都存在匹配,判断最终停留的节点是否为树叶,若是,则返回真,否则返回假。
(4) 输出字符串词频
(5) 删除字符串
首先查找该字符串,边查询边将经过的节点压栈,若找不到,则返回假;否则依次判断栈顶节点是否为树叶,若是则删除该节点,否则返回真。
(6) 输出字典树所有字符串
(7) 计算所有字符串的词频总数(包含重复或不重复)
(8) 计算字典树中所有单词的最长公共前缀及其长度

7. 实现
//使用字典树存储英文单词,使用的结构是26叉字典树。不区分单词的大小写
#include <cstring>
#include <iostream>

/* trie的节点类型 */
template <int Size> //Size为字符表的大小
struct trie_node 
{
    int freq; //当前节点是否可以作为字符串的结尾,如果是freq>0,如果存在重复单词,freq表示该单词的词频
    int node; //子节点的个数
    trie_node *child[Size]; //指向子节点指针

    /* 构造函数 */
    trie_node() : freq(0), node(0) { memset(child, 0, sizeof(child)); }
};

/* trie */
template <int Size, typename Index> //Size为字符表的大小,Index为字符表的哈希函数
class trie 
{
    public:
        /* 定义类型别名 */
        typedef trie_node<Size> node_type;
        typedef trie_node<Size>* link_type;

        /* 构造函数 */
        trie(Index i = Index()) : index(i){ }

        /* 析构函数 */
        ~trie() { clear(); }

        /* 清空 */
        void clear() 
        {
            clear_node(root);
            for (int i = 0; i < Size; ++i)
                root.child[i] = 0;
        }

        /* 插入字符串 */
        template <typename Iterator>
            void insert(Iterator begin, Iterator end) 
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值