Leetcode208 ,820 字典树/Trie树/前缀树介绍以及用途

1.字典树/Trie树/前缀树

字典树又名前缀树,Trie树,是一种存储大量字符串的树形数据结构,相比于HashMap存储,在存储单词(和语种无关,任意语言都可以)的场景上,节省了大量的内存空间。
下图演示了一个保存了8个单词的字典树的结构,8个单词分别是:“A”, “to”, “tea”, “ted”, “ten”, “i”, “in”, “inn”。在这里插入图片描述
怎么理解这颗树呢?你从根节点走到叶子节点,尝试走一下所有的路径。你会发现,每条从根节点到叶子节点的路径都构成了单词(有的不需要走到叶子节点也是单词,比如 “i” 和 “in”)。trie树里的每个节点只需要保存当前的字符就可以了(当然你也可以额外记录别的信息,比如记录一下如果以当前节点结束是否构成单词)。

2.Leetcode208 实现 Trie (前缀树)

在这里插入图片描述

既然让实现前缀树,我们就需要知道前缀树的基本结构,每一个节点都有一个指针数组,指向下一层的节点,同时我们要判断是否是一个完整的单词,我们还需要bool变量。所以Trie树的每一个节点需要两个成员变量

  1. bool变量判断是否是单词的结尾
  2. 指针数组指向下一层的节点。

完整代码如下:

const int MAXN = 26;
class Trie {
public:
    bool is_str; // 标识当前结点是否为一个完整的字符串
    Trie *next[MAXN]; // 下一个结点的指针数组
    Trie() {
        is_str = NULL;
        memset(next,0,sizeof(next));  // 0就代表指针不存在
    }
    
    /** Inserts a word into the trie. */
    void insert(string word) {
        Trie *cur = this; // cur初始化为根结点指针
        for(char w : word){ // 遍历word中的每一个字符
            if(cur->next[w-'a']==NULL){ // 下一个结点不存在,新增一个结点
                Trie* new_node = new Trie();
                cur->next[w-'a'] = new_node;
            }
            cur = cur->next[w-'a'];
        }
        cur->is_str = true; // 当前结点已经是一个完整的字符串了
    }
    
    /** Returns if the word is in the trie. */
    bool search(string word) {
        Trie *cur = this;
        for(char w : word){
            if(cur!=NULL)
                cur = cur->next[w-'a']; // 更新cur指针的指向,使其指向下一个结点
        }
        return (cur!=NULL&&cur->is_str); // cur指针不为空且cur指针指向的结点为一个完整的字符串,则成功找到字符串
    }
    
    /** Returns if there is any word in the trie that starts with the given prefix. */
    bool startsWith(string prefix) {
        Trie *cur = this;
        for(char w : prefix){
            if(cur!=NULL)
                cur = cur->next[w-'a'];
        }
        return (cur!=NULL); // 相比search(),这里只需判断cur指针是否为空就行了
    }
};

另一种构建方式:

class TrieNode { 
public:
    bool is_str;
    TrieNode* next[26];
    TrieNode()
    {
        is_str = false;
        memset(next,0,sizeof(next));
    }
};


class Trie {
    TrieNode* root;
public:
    /** Initialize your data structure here. */
   
    Trie() {
      root = new TrieNode();

    }
    
    /** Inserts a word into the trie. */
    void insert(string word) {
        TrieNode* cur = root;
        for(int i=0;i<word.size();i++)
        {
            if(cur->next[word[i]-'a'])
            {
                cur = cur->next[word[i]-'a'];
            }else
            {
                cur->next[word[i]-'a'] = new TrieNode();
                cur = cur->next[word[i]-'a'];
            }
        }
        cur->is_str = true;

    }
    
    /** Returns if the word is in the trie. */
    bool search(string word) {
        TrieNode* cur = root;
        for(int i=0;i<word.size();i++)
        {
            if(cur->next[word[i]-'a'])
            {
                cur = cur->next[word[i]-'a'];
            }else
            {
                return false;
            }
        }
        return cur->is_str;

    }
    
    /** Returns if there is any word in the trie that starts with the given prefix. */
    bool startsWith(string prefix) {
        TrieNode* cur = root;
        for(int i=0;i<prefix.size();i++)
        {
            if(cur->next[prefix[i]-'a'])
            {
                cur = cur->next[prefix[i]-'a'];
            }else
            {
                return false;
            }
        }
        return (cur!=NULL);


    }
};

3.LeetCode820 单词的压缩编码

题目描述:
在这里插入图片描述

这道题其实就是求出公共后缀,只要是后缀或者前缀都可以通过构建前缀树得方式去解决,对于后缀我们把单词反转从而构建就能够求得共有的后缀问题。
在这里插入图片描述

比如示例中的[“time”, “me”, “bell”]的逆序就是[“emit”, “em”, “lleb”]。我们可以发现em是emit的前缀。所以"em"就可以忽略了。我们必须要先插入单词长的数组,否则会有问题。比如如果我先插入了"em",再插入"emit",会发现两个都可以插入进去,很显然是不对的,所以在插入之前需要先根据单词的长度由长到短排序。

代码如下:

class TrieNode { 
public:
    bool is_new;
    TrieNode* next[26];
    TrieNode()
    {
        is_new = true;
        memset(next,0,sizeof(next));
    }
};


class Trie {
    TrieNode* root;
public:
    /** Initialize your data structure here. */
   
    Trie() {
      root = new TrieNode();

    }
    
    /** Inserts a word into the trie. */
    int insert(string word) {
        bool is_new = false;
        TrieNode* cur = root;
        for(int i=word.size()-1;i>=0;i--)  // 倒着插入单词
        {
            if(cur->next[word[i]-'a'])
            {
                cur = cur->next[word[i]-'a'];
            }else
            {
                cur->next[word[i]-'a'] = new TrieNode();
                cur = cur->next[word[i]-'a'];
                is_new = true;
                
            }
        }
        return is_new?word.size()+1:0;

    }
  
};

class Solution {
public:
    int minimumLengthEncoding(vector<string>& words) {
        if(words.size()<=0)
            return 0;       
        sort(words.begin(),words.end(),cmp);//按照长短排下序
        int res = 0;
        Trie* node = new Trie();
        for(int i=0;i<words.size();i++)
        {
            res+=node->insert(words[i]);            
        }
        return res;
        
    }
    
    bool static cmp(string a,string b)
    {
        return a.size()>b.size();
    }
};

原创文章 28 获赞 34 访问量 2988

猜你喜欢

转载自blog.csdn.net/weixin_42134034/article/details/105717764