字典树
[ code ]

字典树(Trie tree)又称为前缀树,可以高效地存储和查询大量字符串。字典树结构的一个例子如下图(来自维基百科)。

我最近在做leetcode第208题的时候接触到了字典树的概念。以这道题为例:在字典树中,除根节点之外,每个节点都代表一个字符,并且包含一个布尔量”isEnd”来表明该节点是否是某一个字符串的终点,如果是的话,布尔量的值为true。此外,每个节点还包含了一个数组用于存储其指向的子节点。这样一来,从根节点开始到任意一个终点节点的路径就组成了一个字符串。从图中可以看出,具有公共前缀的字符串,比如最右侧的”in”和”inn”,共享了路径。这样能够提高存储和查询的效率。需要注意的是,在字典树中,终点节点不一定是叶子节点。比如”in”的”n”所在的节点不是叶子节点。

插入

向字典树中存入一个字符串a时,首先查询与根节点连接的节点中是否有节点包含a的第一个字符a[0]。如果没有,那就创建一个包含a[0]的新节点与根节点连接,并在该节点之后依次创建包含a[1], a[2],…, a[n-1]的新节点;如果有,那就顺着该节点继续重复以上步骤。最后,需要将代表a[n-1]的终点节点中的”isEnd”设为true。

查询

查询字典树中是含有字符串a时,从根节点开始,沿着路径依次查询是否包含a中的各个字符,如果有一条从根节点开始的路径能够代表a,且该路径的最后一个节点的”isEnd”值为true,那么字典树中包含a。

打了这么多字还是觉得有点乱乱的,下面就直接放leetcode 208题的代码吧:

class Trie {
public:
    Trie() {
        isEnd = false;
		//字典树中只包含小写字母,因此next矩阵的大小为26
        for(int i = 0;i<26;i++)
        {
            //initialize
            next[i] = nullptr;
        }
    }
    
	//插入
    void insert(string word) {
        Trie* root = this;
        int tmp;
        for(char it: word)
        {
            tmp = it-'a';
            if(root->next[tmp] == nullptr)
            {
				//如果子节里不包含相应的字符,就新建一个子节点
                Trie* node = new Trie();
                root->next[tmp] = node;
            }
            root = root->next[tmp];
        }
        root->isEnd = true;
    }
    
	//查找单词
    bool search(string word) {
        Trie* root = this;
        int tmp;
        for(char it: word)
        {
            tmp = it-'a';
            if(root->next[tmp] == nullptr)
                return false;
            root = root->next[tmp];
        }
        return root->isEnd;      
    }
    
	//查找公共前缀
    bool startsWith(string prefix) {
        Trie* root = this;
        int tmp;
        for(char it: prefix)
        {
            tmp = it-'a';
            if(root->next[tmp] == nullptr)
                return false;
            root = root->next[tmp];
        }
        return true;
    }

private:
    bool isEnd; //节点是否是某个单词的结尾
    Trie* next[26]; //该节点指向的子节点
};

/**
 * Your Trie object will be instantiated and called as such:
 * Trie* obj = new Trie();
 * obj->insert(word);
 * bool param_2 = obj->search(word);
 * bool param_3 = obj->startsWith(prefix);
 */

Reference