Trie树的C++实现

简介:

Trie—单词查找树

Trie,又称单词查找树、前缀树,是一种哈希树的变种。应用于字符串的统计与排序,经常被搜索引擎系统用于文本词频统计。

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

优点:
1.查询快。对于长度为m的键值,最坏情况下只需花费O(m)的时间;而BST需要O(m log n)的时间。
2.当存储大量字符串时,Trie耗费的空间较少。因为键值并非显式存储的,而是与其他键值共享子串。

操作:
1.初始化或清空:遍历Trie,删除所有节点,只保留根节点。

2.插入字符串
1).设置当前节点为根节点,设置当前字符为插入字符串中的首个字符;
2).在当前节点的子节点上搜索当前字符,若存在,则将当前节点设为值为当前字符的子节点;否则新建一个值为当前字符的子节点,并将当前结点设置为新创建的节点。
3).将当前字符设置为串中的下个字符,若当前字符为0,则结束;否则转2.

3.查找字符串
搜索过程与插入操作类似,当字符找不到匹配时返回假;若全部字符都存在匹配,判断最终停留的节点是否为树叶,若是,则返回真,否则返回假。

4.删除字符串
首先查找该字符串,边查询边将经过的节点压栈,若找不到,则返回假;否则依次判断栈顶节点是否为树叶,若是则删除该节点,否则返回真。

复制代码
#include <iostream>
using namespace std;

/* trie的节点类型 */
template <int Size> //Size为字符表的大小
struct trie_node 
{
    bool terminable; //当前节点是否可以作为字符串的结尾
    int node; //子节点的个数
    trie_node *child[Size]; //指向子节点指针

    /* 构造函数 */
    trie_node() : terminable(false), 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) 
    {
        link_type cur = &root; //当前节点设置为根节点
        for (; begin != end; ++begin) 
        {
            if (!cur->child[index[*begin]]) //若当前字符找不到匹配,则新建节点
            {
                cur->child[index[*begin]] = new node_type;
                ++cur->node; //当前节点的子节点数加一
            }
            cur = cur->child[index[*begin]]; //将当前节点设置为当前字符对应的子节点
        }
        cur->terminable = true; //设置存放最后一个字符的节点的可终止标志为真
    }

    /* 插入字符串,针对C风格字符串的重载版本 */
    void insert(const char *str)
    {
        insert(str, str + strlen(str)); 
    }

    /* 查找字符串,算法和插入类似 */
    template <typename Iterator>
    bool find(Iterator begin, Iterator end) 
    {
        link_type cur = &root;
        for (; begin != end; ++begin) 
        {
            if (!cur->child[index[*begin]]) 
                return false;
            cur = cur->child[index[*begin]];
        }
        return cur->terminable;
    }

    /* 查找字符串,针对C风格字符串的重载版本 */
    bool find(const char *str) 
    {
        return find(str, str + strlen(str)); 
    }

    /* 删除字符串 */
    template <typename Iterator>
    bool erase(Iterator begin, Iterator end) 
    {
        bool result; //用于存放搜索结果
        erase_node(begin, end, root, result);
        return result;
    }

    /* 删除字符串,针对C风格字符串的重载版本 */
    bool erase(char *str) 
    {    
        return erase(str, str + strlen(str)); 
    }

    /* 按字典序遍历单词树 */
    template <typename Functor>
    void traverse(Functor &execute = Functor()) 
    {
        visit_node(root, execute);
    }

private:
    /* 访问某结点及其子结点 */
    template <typename Functor>
    void visit_node(node_type cur, Functor &execute) 
    {
        execute(cur);
        for (int i = 0; i < Size; ++i) 
        {
            if (cur.child[i] == 0) continue;
            visit_node(*cur.child[i], execute);
        }
    }
    /* 清除某个节点的所有子节点 */
    void clear_node(node_type cur) 
    {
        for (int i = 0; i < Size; ++i) 
        {
            if (cur.child[i] == 0) continue;
            clear_node(*cur.child[i]);
            delete cur.child[i];
            cur.child[i] = 0;
            if (--cur.node == 0) break;
        }
    }

    /* 边搜索边删除冗余节点,返回值用于向其父节点声明是否该删除该节点 */
    template <typename Iterator>
    bool erase_node(Iterator begin, Iterator end, node_type &cur, bool &result) 
    {
        if (begin == end) //当到达字符串结尾:递归的终止条件
        { 
            result = cur.terminable; //如果当前节点可以作为终止字符,那么结果为真
            cur.terminable = false;  //设置该节点为不可作为终止字符,即删除该字符串
            return cur.node == 0;    //若该节点为树叶,那么通知其父节点删除它
        }
        //当无法匹配当前字符时,将结果设为假并返回假,即通知其父节点不要删除它
        if (cur.child[index[*begin]] == 0) return result = false; 
        //判断是否应该删除该子节点
        else if (erase_node((++begin)--, end, *(cur.child[index[*begin]]), result)) 
        { 
            delete cur.child[index[*begin]]; //删除该子节点
            cur.child[index[*begin]] = 0; //子节点数减一
            //若当前节点为树叶,那么通知其父节点删除它
            if (--cur.node == 0 && cur.terminable == false) return true; 
        }
        return false; //其他情况都返回假
    }

    /* 根节点 */
    node_type root;

    /* 将字符转换为索引的转换表或函数对象 */
    Index index;
};

//index function object
class IndexClass
{  
public:
    int operator[](const char key)  
    {  
        return key % 26;  
    }
};

int main()
{
    trie<26,IndexClass> t;
    t.insert("tree");
    t.insert("tea");
    t.insert("A");
    t.insert("ABC");

    if(t.find("tree"))
        cout<<"find tree"<<endl;
    else
        cout<<"not find tree"<<endl;

    if(t.find("tre"))
        cout<<"find tre"<<endl;
    else
        cout<<"not find tre"<<endl;
    
    if(t.erase("tree"))
        cout<<"delete tree"<<endl;
    else
        cout<<"not find tree"<<endl;

    if(t.find("tree"))
        cout<<"find tree"<<endl;
    else
        cout<<"not find tree"<<endl;

    return 0;
}
复制代码

http://en.wikipedia.org/wiki/Trie


    本文转自阿凡卢博客园博客,原文链接:http://www.cnblogs.com/luxiaoxun/archive/2012/09/03/2668611.html,如需转载请自行联系原作者

相关文章
|
7月前
|
监控 算法 数据处理
基于 C++ 的 KD 树算法在监控局域网屏幕中的理论剖析与工程实践研究
本文探讨了KD树在局域网屏幕监控中的应用,通过C++实现其构建与查询功能,显著提升多维数据处理效率。KD树作为一种二叉空间划分结构,适用于屏幕图像特征匹配、异常画面检测及数据压缩传输优化等场景。相比传统方法,基于KD树的方案检索效率提升2-3个数量级,但高维数据退化和动态更新等问题仍需进一步研究。未来可通过融合其他数据结构、引入深度学习及开发增量式更新算法等方式优化性能。
185 17
|
11月前
|
存储 C++
【C++数据结构——树】哈夫曼树(头歌实践教学平台习题) 【合集】
【数据结构——树】哈夫曼树(头歌实践教学平台习题)【合集】目录 任务描述 相关知识 测试说明 我的通关代码: 测试结果:任务描述 本关任务:编写一个程序构建哈夫曼树和生成哈夫曼编码。 相关知识 为了完成本关任务,你需要掌握: 1.如何构建哈夫曼树, 2.如何生成哈夫曼编码。 测试说明 平台会对你编写的代码进行测试: 测试输入: 1192677541518462450242195190181174157138124123 (用户分别输入所列单词的频度) 预
364 14
【C++数据结构——树】哈夫曼树(头歌实践教学平台习题) 【合集】
|
11月前
|
Java C++
【C++数据结构——树】二叉树的基本运算(头歌实践教学平台习题)【合集】
本关任务:编写一个程序实现二叉树的基本运算。​ 相关知识 创建二叉树 销毁二叉树 查找结点 求二叉树的高度 输出二叉树 //二叉树节点结构体定义 structTreeNode{ intval; TreeNode*left; TreeNode*right; TreeNode(intx):val(x),left(NULL),right(NULL){} }; 创建二叉树 //创建二叉树函数(简单示例,手动构建) TreeNode*create
307 12
|
11月前
|
C++
【C++数据结构——树】二叉树的性质(头歌实践教学平台习题)【合集】
本文档介绍了如何根据二叉树的括号表示串创建二叉树,并计算其结点个数、叶子结点个数、某结点的层次和二叉树的宽度。主要内容包括: 1. **定义二叉树节点结构体**:定义了包含节点值、左子节点指针和右子节点指针的结构体。 2. **实现构建二叉树的函数**:通过解析括号表示串,递归地构建二叉树的各个节点及其子树。 3. **使用示例**:展示了如何调用 `buildTree` 函数构建二叉树并进行简单验证。 4. **计算二叉树属性**: - 计算二叉树节点个数。 - 计算二叉树叶子节点个数。 - 计算某节点的层次。 - 计算二叉树的宽度。 最后,提供了测试说明及通关代
191 10
|
11月前
|
存储 算法 测试技术
【C++数据结构——树】二叉树的遍历算法(头歌教学实验平台习题) 【合集】
本任务旨在实现二叉树的遍历,包括先序、中序、后序和层次遍历。首先介绍了二叉树的基本概念与结构定义,并通过C++代码示例展示了如何定义二叉树节点及构建二叉树。接着详细讲解了四种遍历方法的递归实现逻辑,以及层次遍历中队列的应用。最后提供了测试用例和预期输出,确保代码正确性。通过这些内容,帮助读者理解并掌握二叉树遍历的核心思想与实现技巧。
460 3
|
算法 测试技术 C++
【C++】map&set的底层结构 -- AVL树(高度平衡二叉搜索树)(下)
【C++】map&set的底层结构 -- AVL树(高度平衡二叉搜索树)(下)
|
C++ 容器
【C++】map&set的底层结构 -- AVL树(高度平衡二叉搜索树)(上)
【C++】map&set的底层结构 -- AVL树(高度平衡二叉搜索树)(上)
|
存储 C++
【C++】AVL树
AVL树是一种自平衡二叉搜索树,由Georgy Adelson-Velsky和Evgenii Landis提出。它通过确保任意节点的两子树高度差不超过1来维持平衡,支持高效插入、删除和查找操作,时间复杂度为O(log n)。AVL树通过四种旋转操作(左旋、右旋、左-右旋、右-左旋)来恢复树的平衡状态,适用于需要频繁进行数据操作的场景。
434 2
|
存储 C++
【C++】AVL树
AVL树是一种自平衡二叉搜索树:它以苏联科学家Georgy Adelson-Velsky和Evgenii Landis的名字命名。
143 2
|
C++ 容器
【C++航海王:追寻罗杰的编程之路】关联式容器的底层结构——AVL树
【C++航海王:追寻罗杰的编程之路】关联式容器的底层结构——AVL树
150 5