LeetCode题解--208.实现 Trie (前缀树)

一、背景

    摘抄自别人的博客“trie树,即字典树,又称单词查找树或键树,是一种树形结构,是一种哈希树的变种。典型应用是用于统计和排序大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。它的优点是:最大限度地减少无谓的字符串比较,查询效率比哈希表高。”
    总体来讲,前缀树的构造过程,通过不断插入新的字符串来丰富这棵26叉树。强调注意这里是26叉树,因为每一个英文字符串中下一个字母都只能是a-z中的一种可能。
    前缀树的功能很强大,可以做文本词频统计,例如我们在搜索框中的搜索提示,就可以利用前缀树实现。因此,前缀树基本的操作是字符串的插入,搜索,删除,查找前缀等。

二、分析

    这个题就是要求实现前缀树的insert,search以及startwith功能。具体的功能含义在注释中给出了。我们主要对题目的输入解读一下,不然debug的时候完全不知道如何入手。

["Trie","insert","search","search","startsWith","insert","search"]
[[],["apple"],["apple"],["app"],["app"],["app"],["app"]]

    上面那一行字符串表示要执行的操作,其中“Trie”表示构造前缀树,“insert”插入,“search”搜索,“startwith”查找前缀,下面对应的是对应的字符串/前缀参数。
    下面我们针对这个例子,解释一下前缀树的实现。
    前文提到,前缀树是一个26叉树,所以就是有26个next指针,当然也包括一个整型val变量,一会解释这个val的意义。
    所以基本的一个树节点应该是下面这个样子,构造函数应该将val初始化为0。val的含义表示从根节点到当前节点的路径构成的字符串被插入到Trie中的次数

class Node{
public:
    int val;
    Node* next[26];
    Node(){
        val = 0;
        for(int i=0;i<26;i++)
            next[i] = NULL;
    }
};

    前面也提到,一个Trie是通过不断insert新的字符串不断变大的。那么我们关注insert操作如何实现,便知道Trie的构造。以上面的例子为例,在构造完一个根节点后,我们要插入“apple”这个单词。我们要循着“a->p->p->l->e这个路径去逐个确认每一个前缀是否已经存在,如果不存在,就建立出这个前缀的最后一个根节点,如果存在,就继续确认下一个字母”。
《LeetCode题解--208.实现 Trie (前缀树)》
    而当字符串的每一个前缀都确认(查找或建立)完成后,我们需要将这个字符串路径的最后一个节点的val值增1,这样表示这个字符串插入到Trie中1次,如果又插入一遍,那么这个节点的val就变成2,以此类推。
    所以大致的代码结构是下面所示:

void insert(string word){
    Node* p = root; //定义一个指针p,用来搜索这棵树
    for(int i=0;i<word.size();i++){
     //word[i]是当前前缀的最后一个字母,而word[i]-'a'就是在Node类里next数组的下标
        if(p->next[word[i]-'a']==NULL){            
            //定义出这个前缀
            p->next[word[i]-'a'] = new Node();
        }
        p = p->next[word[i]-'a'];  //搜索下一个子串的指针方向
    }
    //for循环执行完,表示这个字符串已经插入完成
    p->val++;   //将这个字符串的尾巴节点val+1
}

    我们再给一个插入的例子,来看上面的代码,假设我们插入“app”。此时的Trie中已经有了“apple这个单词。”
《LeetCode题解--208.实现 Trie (前缀树)》
    search和startwith函数的代码不给出,请读者自己仿照insert思考,注意利用val存储的值以及树的结构。

三、C++实现

class Node{
public:    
    Node* next[26];
    int num;
    Node(){
        num = 0;
        for(int i=0;i<26;i++)
            next[i] = NULL;
    }
};
class Trie {
public:
    /** Initialize your data structure here. */
    Node* root;
    Trie() {
        root = new Node();
    }

    /** Inserts a word into the trie. */
    void insert(string word) {
        //cout<<"inserting "<<word<<endl;
        Node* p = root;
        for(int i=0;i<word.size();i++){
            if(p->next[word[i]-'a']==NULL){
                p->next[word[i]-'a'] = new Node();                  
            }
            p = p->next[word[i]-'a'];
        }
        p->num++;
    }

    /** Returns if the word is in the trie. */
    bool search(string word) {
        //cout<<"searching "<<word<<" ";
        Node* p = root;
        for(int i=0;i<word.size();i++){
            if(p->next[word[i]-'a']==NULL){
                //cout<<"false"<<endl;
                return false;
            }
            p = p->next[word[i]-'a'];
        }
        if(p->num!=0){
            //cout<<"true"<<endl;
            return true;
        }
        else{
            //cout<<"false"<<endl;
            return false;
        }
    }

    /** Returns if there is any word in the trie that starts with the given prefix. */
    bool startsWith(string prefix) {
        //cout<<"start with "<<prefix<<endl;
        Node* p = root;
         for(int i=0;i<prefix.size();i++){
            if(p->next[prefix[i]-'a']==NULL) return false;
            p = p->next[prefix[i]-'a'];
        }        
        return true;
    }
};
    原文作者:Trie树
    原文地址: https://blog.csdn.net/qq_33297776/article/details/82315859
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞