LeetCode-Trie树

1 Trie树

1.1 简介

        Trie 树,也叫“字典树”。顾名思义,它是一个树形结构。它是一种专门处理字符串匹配的数据结构,用来解决在一组字符串集合中快速查找某个字符串的问题。

1.2 实现

        Trie 树是一个多叉树,具体实现如下:

// TrieTree.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <string.h>

class Trie
{
public:
	Trie()
	{
		root = new TrieNode('/'); // 存储无意义字符
	}
	~Trie()
	{
		destroyTrieNode(root);
	}

	// 往Trie树中插入一个字符串
	void insert(const char *text)
	{
		TrieNode *p = root;
		for (int i = 0; i < strlen(text); ++i)
		{
			int index = text[i] - 'a';
			if (p->children[index] == NULL)
			{
				TrieNode *newNode = new TrieNode(text[i]);
				p->children[index] = newNode;
			}

			p = p->children[index];
		}
		p->isEndingChar = true;
	}

	// 在Trie树中查找一个字符串
	bool find(const char *pattern)
	{
		TrieNode *p = root;
		for (int i = 0; i < strlen(pattern); ++i)
		{
			int index = pattern[i] - 'a';
			if (p->children[index] == NULL)
			{
				return false; // 不存在pattern
			}

			p = p->children[index];
		}

		if (p->isEndingChar == false)
		{
			return false; // 不能完全匹配,只是前缀
		}
		else
		{
			return true; // 找到pattern
		}
	}

	//删除trie中的字符串时,把要删除的字符串在trie树中的ending标记删除就好了
	void del(const char *pattern)
	{
		TrieNode *p = root;
		for (int i = 0; i < strlen(pattern); ++i)
		{
			int index = pattern[i] - 'a';
			if (p->children[index] == NULL)
			{
				return; // 不存在pattern
			}

			p = p->children[index];
		}

		if (p->isEndingChar == false)
		{
			return; // 不能完全匹配,只是前缀
		}
		else
		{
			// 找到pattern
			p->isEndingChar = false;
			return;
		}
	}

private:
	class TrieNode;
	void destroyTrieNode(TrieNode *node)
	{
		if (node && !node->isEndingChar)
		{
			for (int i = 0; i < 26; ++i)
			{
				destroyTrieNode(node->children[i]);
			}

			delete[] node->children;
		}
	}

private:
	class TrieNode
	{
	public:
		TrieNode(char value = '*') : data(value), isEndingChar(false)
		{
			children = new TrieNode*[26];
			for (int i = 0; i < 26; ++i)
			{
				children[i] = NULL;
			}
		}
	public:
		char data;
		TrieNode **children;
		bool isEndingChar;
	};

private:
	TrieNode *root;
};

int main()
{
	//test 1
	//Trie trie;
	//trie.insert("how");
	//trie.insert("hi");
	//trie.insert("her");
	//trie.insert("hello");
	//trie.insert("so");
	//trie.insert("see");


	//bool bFind = trie.find("hi");
	//bFind = trie.find("he");
	//bFind = trie.find("soo");
	//bFind = trie.find("see");

	//trie.del("soo");
	//trie.del("see");
	//bFind = trie.find("see");

	//test 2
	Trie trie2;
	trie2.insert("hey");
	trie2.insert("he");

	bool bFind2 = trie2.find("he");
	bFind2 = trie2.find("hey");

	return 0;
}

2 相关题目

2.1 实现前缀树

剑指 Offer II 062. 实现前缀树

208. 实现 Trie (前缀树)

class Trie {
private:
    class TrieNode
    {
        public:
        TrieNode(char c='*') : data(c), isEndingChar(false)
        {
            child = new TrieNode*[26];
            for (int i=0;i<26;++i)
            {
                child[i] = NULL;
            }
        }

        public:
        char data;
        bool isEndingChar;
        TrieNode** child;
    };
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.length();++i)
        {
            int id = word[i] - 'a';
            if (!cur->child[id])
            {
                cur->child[id] = new TrieNode(word[i]);
            }
            cur = cur->child[id];
        }
        cur->isEndingChar = true;
    }
    
    /** Returns if the word is in the trie. */
    bool search(string word) {
        TrieNode* cur = root;
        for (int i=0;i<word.length();++i)
        {
            int id = word[i] - 'a';
            if (!cur->child[id])
            {
                return false;
            }
            cur = cur->child[id];            
        }

        if (cur->isEndingChar)
            return true;
        else
            return false;
    }
    
    /** 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.length();++i)
        {
            int id = prefix[i] - 'a';
            if (!cur->child[id])
            {
                return false;
            }
            cur = cur->child[id];            
        }

        return true;
    }

private:
    TrieNode* root;    
};

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

2.2 替换单词

剑指 Offer II 063. 替换单词

648. 单词替换

class Solution {
public:
    string replaceWords(vector<string>& dictionary, string sentence) {
        //创建字典树
        Trie* pTrie = new Trie;
        for (string &word: dictionary) 
        {
            pTrie->insert(word);
        }

        //分割单词
        string newSentence;
        istringstream iss(sentence);
        string word;
        while (iss >> word) 
        {
            pTrie->replace(word);
            if (newSentence.empty())
            {
                newSentence.append(word);
            }
            else
            {
                newSentence.append(1,' ').append(word);
            }
        }
        
        return newSentence;
    }
 
private:
    class Trie {
    public:
        Trie(): root(new TrieNode()) {}

        void insert (string &word) 
        {
            TrieNode* p = root.get();
            for (char &c: word) 
            {
                if (!p->childs.count(c)) 
                {
                    p->childs[c] = new TrieNode();  
                }
                p = p->childs[c];
            }
            p->word = &word;
        }

        void replace (string &word) 
        {
            TrieNode* p = root.get();
            for (char &c: word) 
            {
                //找到了词根,修改word指向词根
                if (p && p->word) 
                {
                    word = *(p->word);
                    break;
                }   
                if (!p->childs.count(c)) 
                {
                    break;
                } 
                p = p->childs[c];                        
            }

        }

    private:
        struct TrieNode 
        {
            TrieNode(): word(nullptr) {};
            ~TrieNode() 
            { 
                for (auto& child: childs) 
                {
                    if (child.second) 
                    { 
                        delete child.second;
                    } 
                }
            }
                
            string *word;
            map<char, TrieNode*> childs;
        };

        std::unique_ptr<TrieNode> root;
    };

};

2.3 神奇的字典

676. 实现一个魔法字典

剑指 Offer II 064. 神奇的字典

class MagicDictionary {
private:
    class Trie {
    private:
        struct TrieNode 
        {
            TrieNode(): isEnding(false) {};
            ~TrieNode() 
            { 
                for (auto& child: childs) 
                {
                    if (child.second) 
                    { 
                        delete child.second;
                    } 
                }
            }
                
            bool isEnding;
            unordered_map<char, TrieNode*> childs;
        };        
    public:
        Trie(): root(new TrieNode()) {}
        void insert (const string& word) 
        {
            TrieNode* cur = root;
            for (auto& c: word) 
            {
                if (!cur->childs.count(c))
                {
                    cur->childs[c] = new TrieNode();  
                }
                cur = cur->childs[c];
            }
            cur->isEnding = true;
        }

        bool search (const string& word) 
        {
            TrieNode* cur = root;
            for (int i=0;i<word.length();++i) 
            {
                char c = word[i];
                if (!cur->childs.count(c))
                {
                    for (auto& child : cur->childs)
                    {
                        if (search(child.second, word.substr(i+1)))
                            return true;
                    }

                    return false;
                }
                else
                {
                    for (auto& child : cur->childs)
                    {
                        if (child.first != c && search(child.second, word.substr(i+1)))
                            return true;
                    }                        
                    cur = cur->childs[c];
                }                                      
            }

           return false;
        }

        bool search(TrieNode* child, const string& word)
        {
            TrieNode* cur = child;
            for (auto& c: word) 
            {
                if (!cur->childs.count(c))
                    return false;
                cur = cur->childs[c];                      
            }

            if (cur->isEnding)
                return true;
            else
                return false;
        }

    private:
        TrieNode* root;
    };

private:
    Trie* trie;
public:
    /** Initialize your data structure here. */
    MagicDictionary() {
        trie = new Trie();
    }
    
    void buildDict(vector<string> dictionary) {
        for (auto& str : dictionary)
            trie->insert(str);
    }
    
    bool search(string searchWord) {
        return trie->search(searchWord);
    }  
};

/**
 * Your MagicDictionary object will be instantiated and called as such:
 * MagicDictionary* obj = new MagicDictionary();
 * obj->buildDict(dictionary);
 * bool param_2 = obj->search(searchWord);
 */

2.4 单词的压缩编码

820. 单词的压缩编码

剑指 Offer II 065. 最短的单词编码

class Solution {
private:
    class Trie {
    private:
        struct TrieNode 
        {
            TrieNode(): isEnding(false) {};
            ~TrieNode() 
            { 
                for (auto& child: childs) 
                {
                    if (child.second) 
                    { 
                        delete child.second;
                    } 
                }
            }

            bool isEnding;
            unordered_map<char, TrieNode*> childs;
            unordered_map<string, int> mLen;
        };        
    public:
        Trie(): root(new TrieNode()) {}
        void insert (const string& word) 
        {
            TrieNode* cur = root;
            for (int i=0;i<word.length();++i) 
            {
                char c = word[i];
                if (!cur->childs.count(c))
                {
                    cur->childs[c] = new TrieNode();  
                }
                cur = cur->childs[c];
                if (cur->isEnding)
                {
                    root->mLen.erase(word.substr(0,i+1));
                }
            }
            cur->isEnding = true;
            if (cur->childs.empty())
            {
                root->mLen[word] = word.length();
            }
        }

        bool search(TrieNode* child, const string& word)
        {
            TrieNode* cur = child;
            for (auto& c: word) 
            {
                if (!cur->childs.count(c))
                    return false;
                cur = cur->childs[c];                      
            }

            if (cur->isEnding)
                return true;
            else
                return false;
        }

        int getMaxLen()
        {
            int maxLen = 0;            
            for (auto& item : root->mLen)
            {
                maxLen += item.second;
            }

            return maxLen + root->mLen.size();
        }

    private:
        TrieNode* root;
    };

public:
    int minimumLengthEncoding(vector<string>& words) {
        Trie trie;
        for(auto& str:words)
        {
            reverse(str);
            trie.insert(str);
        }
        
        return trie.getMaxLen();
    }

    void reverse(string& str)
    {
        int i=0, j=str.length()-1;
        while(i<j)
        {
            char c = str[i];
            str[i] = str[j];
            str[j] = c;
            ++i;
            --j;
        }
    }
};

2.5 单词之和

剑指 Offer II 066. 单词之和

677. 键值映射

class MapSum {
private:
    class Trie {
    private:
        struct TrieNode 
        {
            TrieNode(): sum(0), isEnding(false) {};
            ~TrieNode() 
            { 
                for (auto& child: childs) 
                {
                    if (child.second) 
                    { 
                        delete child.second;
                    } 
                }
            }
            int sum;
            bool isEnding;
            unordered_map<char, TrieNode*> childs;
        }; 
    public:
        Trie(): root(new TrieNode()) {}
        void insert (const string& key, int val) 
        {
            int sum = val;
            if (mStr2Val.count(key))
            {
                sum = val - mStr2Val[key];
            }
            
            mStr2Val[key] = val;
            TrieNode* cur = root;
            for (auto& c:key) 
            {
                if (!cur->childs.count(c))
                {
                    cur->childs[c] = new TrieNode();  
                }
                cur = cur->childs[c];
                cur->sum += sum;
            }
            cur->isEnding = true;
        }

        int getSum(const string& key)
        {
            TrieNode* cur = root;
            for (auto& c:key) 
            {
                if (!cur->childs.count(c))
                {
                    return 0;  
                }
                cur = cur->childs[c];
            }

            return cur->sum;
        }

    private:
        TrieNode* root;
        unordered_map<string, int> mStr2Val;
    };

private:
    Trie *trie;
public:
    /** Initialize your data structure here. */
    MapSum() {
        trie = new Trie;
    }
    
    void insert(string key, int val) {
        trie->insert(key, val);
    }
    
    int sum(string prefix) {
        return trie->getSum(prefix);
    }
};

/**
 * Your MapSum object will be instantiated and called as such:
 * MapSum* obj = new MapSum();
 * obj->insert(key,val);
 * int param_2 = obj->sum(prefix);
 */

2.6

2.7

2.8

2.9

2.10

2.11

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值