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 实现前缀树
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 替换单词
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 神奇的字典
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 单词的压缩编码
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 单词之和
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