前言
这道题做完,成就感还是满满当当的,嘿嘿!(虽然时间复杂度不是很好,但是空间复杂度很好看)
题目描述
Trie(发音类似 “try”)或者说 前缀树 是一种树形数据结构,用于高效地存储和检索字符串数据集中的键。这一数据结构有相当多的应用情景,例如自动补完和拼写检查。
请你实现 Trie 类:
Trie() 初始化前缀树对象。
void insert(String word) 向前缀树中插入字符串 word 。
boolean search(String word) 如果字符串 word 在前缀树中,返回 true(即,在检索之前已经插入);否则,返回 false 。
boolean startsWith(String prefix) 如果之前已经插入的字符串 word 的前缀之一为 prefix ,返回 true ;否则,返回 false 。
示例:
输入
[“Trie”, “insert”, “search”, “search”, “startsWith”, “insert”, “search”]
[[], [“apple”], [“apple”], [“app”], [“app”], [“app”], [“app”]]
输出
[null, null, true, false, true, null, true]
解释
Trie trie = new Trie();
trie.insert(“apple”);
trie.search(“apple”); // 返回 True
trie.search(“app”); // 返回 False
trie.startsWith(“app”); // 返回 True
trie.insert(“app”);
trie.search(“app”); // 返回 True
提示:
1 <= word.length, prefix.length <= 2000
word 和 prefix 仅由小写英文字母组成
insert、search 和 startsWith 调用次数 总计 不超过 3 * 104 次
思路
主要是创建一个不固定大小的树,节点的结构为:
struct TreeNode {
char val;
vector<struct TreeNode*> children;
bool flag = false;
};
之所以设置flag,是为了防止插入重复的字符串部分(比如先插入apple,再插入app)被错误判断.
1)insert函数:我们传入三个参数(string& word, int index, TreeNode* Node),当我们当前的word[index]存在于树中时,我们就递归进入下一层,如果我们是先插入”apple”,再插入”app”,那么我们在最后一个’p’节点的位置,要设置flag=true,这是精髓所在.
void DC(string& word, int index, TreeNode* Node) { //创造节点
if (index >= word.size()&&Node==NULL) return;
else if (index == word.size()) {
Node->flag = true;
return;
}
for (int i = 0; i < Node->children.size(); ++i) {
if (Node->children[i]->val == word[index]) {
DC(word, index + 1, Node->children[i]);
return;
}
}
TreeNode* temp = new TreeNode();
temp->val = word[index];
Node->children.push_back(temp);
DC(word, index + 1, temp);
}
2)search函数:搜索函数返回结果的情况有两种:第一种是查找到底部(eg:insert(“apple”); search(“apple”)),第二种是查找到index==word.size()时,判断Node->flag是不是为true,为true表示从根节点到当前节点的他们的字符之和为app,那么也可以直接返回true
bool Sr(string& word, int index,TreeNode* Node) {
if (index == word.size()) {
if(Node->children.size() == 0) return true;
else {
if (Node->flag) return true;
}
}
if (index == word.size()) return false;
for (int i = 0; i < Node->children.size(); ++i) {
if (Node->children[i]->val == word[index]) {
return Sr(word, index + 1, Node->children[i]);
}
}
return false;
}
3)searchWith函数:当index>=word.size()时表示一定存在一条满足条件的路径(这比search函数好理解地多),注意参数中的vec数组表示上一个节点的孩子节点.
bool SW(string& word, int index, vector<TreeNode*> vec) {
if (index >= word.size()) return true;
for (int i = 0; i < vec.size(); ++i) {
if (vec[i]->val == word[index]) {
return SW(word, index + 1, vec[i]->children);
}
}
return false;
}
代码
#include<vector>
#include<string>
using namespace std;
class Trie {
public:
struct TreeNode {
char val;
vector<struct TreeNode*> children;
bool flag = false;
};
TreeNode* head;
Trie() {
head = new TreeNode();
head->val = '#'; //设置#表示头结点,'@'表示NULL
head->flag = true; //设置flag表示以当前节点作为结尾的字符串是否是存在的
}
void DC(string& word, int index, TreeNode* Node) { //创造节点
if (index >= word.size()&&Node==NULL) return;
else if (index == word.size()) {
Node->flag = true;
return;
}
for (int i = 0; i < Node->children.size(); ++i) {
if (Node->children[i]->val == word[index]) {
DC(word, index + 1, Node->children[i]);
return;
}
}
TreeNode* temp = new TreeNode();
temp->val = word[index];
Node->children.push_back(temp);
DC(word, index + 1, temp);
}
void insert(string word) {
DC(word, 0, head); //dfs+create=DC
}
bool SW(string& word, int index, vector<TreeNode*> vec) {
if (index >= word.size()) return true;
for (int i = 0; i < vec.size(); ++i) {
if (vec[i]->val == word[index]) {
return SW(word, index + 1, vec[i]->children);
}
}
return false;
}
bool Sr(string& word, int index,TreeNode* Node) {
if (index == word.size()) {
if(Node->children.size() == 0) return true;
else {
if (Node->flag) return true;
}
}
if (index == word.size()) return false;
for (int i = 0; i < Node->children.size(); ++i) {
if (Node->children[i]->val == word[index]) {
return Sr(word, index + 1, Node->children[i]);
}
}
return false;
}
bool search(string word) {
return Sr(word, 0, head);
}
bool startsWith(string prefix) {
return SW(prefix, 0, head->children); //dfs+find=DF
}
};
//int main() {
// Trie* obj = new Trie();
// obj->insert("apple");
// obj->insert("app");
// bool param_2 = obj->search("app");
// bool param_3 = obj->startsWith("app");
// return 0;
//}