Trie

参考

https://en.wikipedia.org/wiki/Trie

a trie, also called digital tree or prefix tree, is a kind of search tree—an ordered tree data structure used to store a dynamic set or associative array where the keys are usually strings

也叫前缀树, 特定情况下替代set 和map。这时候key是字符串。


image

LeetCode:208. Implement Trie (Prefix Tree)
LeetCode:211. Add and Search Word - Data structure design
LeetCode:212. Word Search II


下面我们就一步步来实现Trie,并做题

一、初步实现Trie树结构


208. Implement Trie (Prefix Tree)

https://leetcode.com/problems/implement-trie-prefix-tree/description/


非递归实现


#include<cstdio>

#include<iostream>
#include<string>
#include<vector>
#include<algorithm>
using namespace std;


/**
 * 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);
 */

const int letter_size=26;//字符集大小
struct TrieNode
{
    TrieNode* children[letter_size]={0};
    bool isWord = false;
    TrieNode(){
    }
    ~TrieNode(){
        for(int i=0; i<letter_size; i++) if(children[i]) delete children[i];
    }
};

struct Trie {
    TrieNode* root;

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

    ~Trie(){
        delete root;
    }

    int idx(char c)
    {
        return c - 'a';
    }

    /** Inserts a word into the trie. */
    void insert(string word) {
        TrieNode*p = root;
        int len = word.length();
        for(int i=0; i < len; i++){
            int c = idx(word[i]);
            if(!p->children[c])
                p->children[c] = new TrieNode();
            p = p->children[c];
        }
        p->isWord = true;
    }

    //辅助函数
    TrieNode * _search(string &word)
    {
        TrieNode *p=root;
        int len = word.length();
        for(int i=0; i < len; i++){
            TrieNode *t = p->children[idx(word[i])];
            if(!t)
                return 0;
            else
                p = t;
        }
        return p;
    }

    /** Returns if the word is in the trie. */
    bool search(string word) {
        TrieNode *p=_search(word);
        return p && p->isWord;
    }

    /** Returns if there is any word in the trie that starts with the given prefix. */
    bool startsWith(string word) {
        return _search(word);;
    }
};




int main()
{
    Trie trie;

    trie.insert("apple");
    cout<< trie.search("apple") << endl;   // returns true
    cout<< trie.search("app") << endl;     // returns false
    cout<< trie.startsWith("app") << endl; // returns true
    trie.insert("app");
    cout<< trie.search("app") << endl;;     // returns true

    return 0;
}


递归实现:

#include<cstdio>

#include<iostream>
#include<string>
#include<vector>
#include<algorithm>
using namespace std;


/**
 * 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);
 */

const int letter_size=26;//字符集大小
struct TrieNode
{
    TrieNode* children[letter_size]={0};
    bool isWord = false;
    TrieNode(){
    }
    ~TrieNode(){
        for(int i=0; i<letter_size; i++) if(children[i]) delete children[i];
    }
};

struct Trie {
    TrieNode* root;

    /** Initialize your data structure here. */
    Trie() {
        root=new TrieNode();
    }
    // OJ上去掉析构会快很多
    ~Trie(){
        delete root;
    }

    int idx(char c)
    {
        return c - 'a';
    }

    /** Inserts a word into the trie. */
    void insert(string word) {
        TrieNode*p = root;
        int len = word.length();
        for(int i=0; i < len; i++){
            int c = idx(word[i]);
            if(!p->children[c])
                p->children[c] = new TrieNode();
            p = p->children[c];
        }
        p->isWord = true;
    }

    TrieNode* dfs(TrieNode *root, string &word, int pos)
    {
        if(pos==word.length())
            return root;

        int c = word[pos];
        TrieNode* p = root->children[idx(c)];
        if(p)
            return dfs(p, word, pos+1);
        else
            return 0;
    }

    /** Returns if the word is in the trie. */
    bool search(string word) {
        TrieNode *p = dfs(root, word, 0);
        return p && p->isWord;
    }

    /** Returns if there is any word in the trie that starts with the given prefix. */
    bool startsWith(string word) {
        TrieNode *p = dfs(root, word, 0);
        return p!=0;
    }
};




int main()
{
    Trie trie;

    trie.insert("apple");
    cout<< trie.search("apple") << endl;   // returns true
    cout<< trie.search("app") << endl;     // returns false
    cout<< trie.startsWith("app") << endl; // returns true
    trie.insert("app");
    cout<< trie.search("app") << endl;;     // returns true

    return 0;
}


二、Trie树的运用:Trie树+回溯法

充分理解这道题后,就可以接着将这种数据结构投入运用了。

这一道题和上一题类似,需要你实现两个API,一个 插入,一个查找。插入和上一题相同,查找则需要实现“模糊查找”即:
addWord("bad")
addWord("dad")
addWord("mad")
search("pad") -> false
search("bad") -> true
search(".ad") -> true
search("b..") –> true

https://leetcode.com/problems/add-and-search-word-data-structure-design


#include<cstdio>

#include<iostream>
#include<string>
#include<vector>
#include<algorithm>
using namespace std;


/**
 * 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);
 */

const int letter_size=26;//字符集大小
struct TrieNode
{
    TrieNode* children[letter_size]={0};
    bool isWord = false;
    TrieNode(){
    }
    ~TrieNode(){
        for(int i=0; i<letter_size; i++) if(children[i]) delete children[i];
    }
};

struct Trie {
    TrieNode* root;

    /** Initialize your data structure here. */
    Trie() {
        root=new TrieNode();
    }
    // OJ上去掉析构会快很多
    ~Trie(){
        delete root;
    }

    int idx(char c)
    {
        return c - 'a';
    }

    /** Inserts a word into the trie. */
    void insert(string word) {
        TrieNode*p = root;
        int len = word.length();
        for(int i=0; i < len; i++){
            int c = idx(word[i]);
            if(!p->children[c])
                p->children[c] = new TrieNode();
            p = p->children[c];
        }
        p->isWord = true;
    }

    TrieNode* dfs(TrieNode *root, string &word, int pos)
    {
        if(pos==word.length())
            return root;

        int c = word[pos];
        // . 的话,匹配所有子节点
        if(c=='.')
        {
            for(auto p : root->children)
            {
                if(p)
                {
                    TrieNode* t = dfs(p, word, pos+1);
                    if(t && t->isWord) return t;//如果找到一个,直接返回(需要判断是否是word,否则可能返回中间节点)
                }
            }
        }
        else// 否则,正常匹配
        {
            TrieNode* p = root->children[idx(c)];
            if(p)
                return dfs(p, word, pos+1);
            else
                return 0;
        }
        return 0;
    }

    /** Returns if the word is in the trie. */
    bool search(string word) {
        TrieNode *p = dfs(root, word, 0);
        return p && p->isWord;
    }

//    /** Returns if there is any word in the trie that starts with the given prefix. */
//    bool startsWith(string word) {
//        TrieNode *p = dfs(root, word, 0);
//        return p!=0;
//    }
};



//["WordDictionary","addWord","addWord","addWord","addWord","addWord","addWord","addWord","addWord","search","search","search","search","search","search","search","search","search","search"]
//[[],["ran"],["rune"],["runner"],["runs"],["add"],["adds"],["adder"],["addee"],["r.n"],["ru.n.e"],["add"],["add."],["adde."],[".an."],["...s"],["....e."],["......."],["..n.r"]]
int main()
{
    Trie trie;
    vector<string> input={"ran","rune","runner","runs","add","adds","adder","addee"};
    for(string s: input)
    {
        cout<<s<<endl;
        trie.insert(s);
    }
    vector<string> search={"r.n","ru.n.e","add","add.","adde.",".an.","...s","....e.",".......","..n.r"};
    for(string s: search)
    {
        cout<< trie.search(s) << endl;
    }

    cout<< trie.search("add.") << endl;   // returns true


    return 0;
}

三、Trie树的运用:Trie树+深度优先搜索

Word Search II - LeetCode​leetcode.com

此题咋一看,很简单嘛,把针对每个单词进行一次深度优先搜索不就行了?这个思路是可行的,但十有八九会超时。因为深度优先搜索本身就是一种比较耗时间和内存的算法。

但这道题似乎也只好用深度优先搜索,那么,有没有可能只进行一次搜索,就查明白所有的单词是否存在于这个二维矩阵中?

是的,这个时候你需要Trie树。而且题目也有提示,你看,output里面不是按字典顺序输出的吗?哈哈。 所以思路如下:

1、所有单词建立Trie树

2、dfs每个board位置, 去和Trie路径匹配。Tire当前位置有这个字母时候,才继续dfs下去。

Example:

Input: 
board = [
  ['o','a','a','n'],
  ['e','t','a','e'],
  ['i','h','k','r'],
  ['i','f','l','v']
]
words = ["oath","pea","eat","rain"]

Output: ["eat","oath"]


#include<cstdio>

#include<iostream>
#include<string>
#include<vector>
#include<algorithm>
using namespace std;


class TrieNode{//树节点结构
public:
    TrieNode*next[26]={0};
    int isexist = -1;//-1表示节点表示的单词不存在,0及以上的数表示此单词存在且与vector<string>>words的序号对应
    TrieNode(){

    }
    ~TrieNode(){
        for(int i=0;i<26;i++){
            if(next[i])delete next[i];
        }
    }
};

#include <unordered_set>

class Solution {
public:
    TrieNode* root=0;
    int arr[4][2]={{1,0},{-1,0},{0,-1},{0,1}};//搜索的四个方向

    int idx(char c)
    {
        return c - 'a';
    }

    /** Inserts a word into the trie. */
    void insert(string word, int index) {
        TrieNode*p = root;
        int len = word.length();
        for(int i=0; i < len; i++){
            int c = idx(word[i]);
            if(!p->next[c])
                p->next[c] = new TrieNode();
            p = p->next[c];
        }
        p->isexist = index;
    }


    void dfs(int r,int c,TrieNode*pNode,unordered_set<int>&res,vector<vector<char>>b){
        b[r][c]='0';//标记已经搜索过得地方为‘0’
        if(pNode && pNode->isexist>=0){//如果isexist>=0表示word[isexist]存在于此节点。
            res.insert(pNode->isexist);
        }
        for(int i=0;i<4;i++){//向上下左右四个方向搜索
            int newr=r+arr[i][0],newc=c+arr[i][1];
            if(newr>=0&&newc>=0&&newr<b.size()&&newc<b[0].size()&&b[newr][newc]!='0'&&pNode->next[b[newr][newc]-'a']){
                dfs(newr,newc,pNode->next[b[newr][newc]-'a'],res,b);
            }
        }
    }

    vector<string> findWords(vector<vector<char>>& board, vector<string>& words) {
        //所有单词建立Tire
        root=new TrieNode();
        for(int i=0; i<words.size(); i++)
            insert(words[i], i);

        unordered_set<int>res;//用于保存在board中查找到的words的序号
        for(int r=0;r<(int)board.size();r++){
            for(int c=0;c<(int)board[r].size();c++){
                if(root->next[board[r][c]-'a'])dfs(r,c,root->next[board[r][c]-'a'],res,board);
            }
        }

        vector<string>ress;//根据res中的序号制作string数组返回
        for(auto it:res){
            ress.push_back(words[it]);
        }
        return ress;
    }

};

int main()
{
    vector<vector<char>>  board={
        {'o','a','a','n'},
        {'e','t','a','e'},
        {'i','h','k','r'},
        {'i','f','l','v'}
    };
    vector<string> words = {"oath","pea","eat","rain"};

    Solution s;
    vector<string> result = s.findWords(board, words);
    for(auto it:result){
        cout << it <<endl;
    }

    return 0;
}



posted on 2020-06-08 17:05  katago  阅读(336)  评论(0编辑  收藏  举报