高级树形结构之字典树(Trie)

1.定义

字典树又称单词查找树,前缀树,Trie树,是一种树形结构,是一种哈希树的变种。典型应用是用于统计,排序和保存大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。它的优点是:利用字符串的公共前缀来减少查询时间,最大限度地减少无谓的字符串比较,查询效率比哈希树高。即主要用于词频统计与查找(前缀匹配)

2.Trie的构建

下面我们有and,as,at,cn,com这些关键词,那么如何构建trie树呢?

从上面的图中,我们或多或少的可以发现一些好玩的特性。

      第一:根节点不包含字符,除根节点外的每一个子节点都只包含一个字符。

      第二:从根节点到某一节点,路径上经过的字符连接起来,就是该节点对应的字符串。

      第三:每个节点的子节点包含的字符都不相同,每个单词的公共前缀作为一个字符节点保存

3.Trie基本操作

其基本操作有:查找、插入和删除,其中删除操作比较少见。

4.Tire的优势

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

5.Tire的实现

C++实现:

#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://blog.csdn.net/luxiaoxun/article/details/7937589

http://baike.baidu.com/link?url=d8XtiL2lNfTHWliDS3MEKNh2bjSa2Ue8QiV-4uwXzr0ZgP1oJYmiR5ksny1vd0yj7_7vMo3Mnhts1j4h9JbvHa

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值