• trie树


    描述摘自网络:

    http://www.cnblogs.com/dolphin0520/archive/2011/10/11/2207886.html

     Trie树

           Trie树也称字典树,因为其效率很高,所以在在字符串查找、前缀匹配等中应用很广泛,其高效率是以空间为代价的。

    一.Trie树的原理

        利用串构建一个字典树,这个字典树保存了串的公共前缀信息,因此可以降低查询操作的复杂度。

        下面以英文单词构建的字典树为例,这棵Trie树中每个结点包括26个孩子结点,因为总共有26个英文字母(假设单词都是小写字母组成)。

        则可声明包含Trie树的结点信息的结构体:

    struct TrieNode
    {
        bool isWord;
        TrieNode *next[MAX];
    };

    其中next是一个指针数组,存放着指向各个孩子结点的指针。

        如给出字符串"abc","ab","bd","dda",根据该字符串序列构建一棵Trie树。则构建的树如下:

        

    Trie树的根结点不包含任何信息,第一个字符串为"abc",第一个字母为'a',因此根结点中数组next下标为'a'-97的值不为 NULL,其他同理,构建的Trie树如图所示,红色结点表示在该处可以构成一个单词。很显然,如果要查找单词"abc"是否存在,查找长度则为 O(len),len为要查找的字符串的长度。而若采用一般的逐个匹配查找,则查找长度为O(len*n),n为字符串的个数。显然基于Trie树的查找 效率要高很多。

    但是却是以空间为代价的,比如图中每个结点所占的空间都为(26*4+1)Byte=105Byte,那么这棵Trie树所占的空间则为105*8Byte=840Byte,而普通的逐个查找所占空间只需(3+2+2+3)Byte=10Byte。

    二.Trie树的操作

        在Trie树中主要有3个操作,插入、查找和删除。一般情况下Trie树中很少存在删除单独某个结点的情况,因此只考虑删除整棵树。

    1.插入

      假设存在字符串str,Trie树的根结点为root。i=0,p=root。

      1)取str[i],判断p->next[str[i]-97]是否为空,若为空,则建立结点temp,并将p->next[str[i]-97]指向temp,然后p指向temp;

       若不为空,则p=p->next[str[i]-97];

      2)i++,继续取str[i],循环1)中的操作,直到遇到结束符'\0',此时将当前结点p中的isStr置为true。

    2.查找

      假设要查找的字符串为str,Trie树的根结点为root,i=0,p=root

      1)取str[i],判断判断p->next[str[i]-97]是否为空,若为空,则返回false;若不为空,则p=p->next[str[i]-97],继续取字符。

      2)重复1)中的操作直到遇到结束符'\0',若当前结点p不为空并且isStr为true,则返回true,否则返回false。

    3.删除

      删除可以以递归的形式进行删除。

    代码自己实现:

    #include <iostream>
    #include <cassert>
    using namespace std;
    
    #define MAX 26
    
    struct TrieNode
    {
        bool isWord;
        TrieNode *next[MAX];
    };
    
    TrieNode* createNewNode()
    {
        TrieNode* pNew = new TrieNode;
        pNew->isWord=false;
        for (int i=0; i<MAX; i++)
        {
            pNew->next[i] = NULL;
        }
        return pNew;
    }
    
    //向trie tree中添加单词
    void insertWord(TrieNode* pRoot, const char* word)
    {
        assert(pRoot!=NULL);
        assert(word!=NULL);
        TrieNode* p = pRoot;
        while (*word!='\0')
        {
            if (p->next[*word-'a']==NULL)
            {
                TrieNode* pNext = createNewNode();
                p->next[*word-'a'] = pNext;
            }
            p=p->next[*word-'a'];
            word++;
        }
        p->isWord=true;
    
    }
    
    
    // 查找某单词是否存在
    bool searchWord(TrieNode* pRoot, const char *word)
    {
        assert(pRoot!=NULL);
        assert(word!=NULL);
        TrieNode* p = pRoot;
        while(p!=NULL && *word!='\0')
        {
            p=p->next[*word-'a'];
            word++;
        }
        return (p!=NULL && p->isWord == true);
    }
    
    // 删除trie tree
    void deleteTree(TrieNode *pRoot)
    {
        if (pRoot==NULL)
        {
            return;
        }
        for (int i=0; i<MAX; i++)
        {
            deleteTree(pRoot->next[i]);
        }
        delete pRoot;
    
    }
    
    
    int main(int arrgc, char* argv[])  
    {  
        // 建立根节点
        TrieNode* pRoot = createNewNode();
        
        insertWord(pRoot,"hello");
        insertWord(pRoot,"helloboy");
        insertWord(pRoot,"boy");
        insertWord(pRoot,"world");
    
        cout<<searchWord(pRoot,"boy");
    
        deleteTree(pRoot);
        return 0;
    }  
  • 相关阅读:
    主键索引就是聚集索引吗?
    聚集索引以及非聚集索引
    IO阻塞模型、IO非阻塞模型、多路复用IO模型
    Log4j的使用说明
    前置机是什么
    转:图文理解区块链
    DQL、DML、DDL、DCL全名是啥?
    OLAP和OLTP的区别
    JAVA之运算符优先级
    JAVA之异常处理(一)
  • 原文地址:https://www.cnblogs.com/cyttina/p/2746094.html
Copyright © 2020-2023  润新知