208. 实现 Trie (前缀树)
实现一个 Trie (前缀树),包含
insert
,search
, 和startsWith
这三个操作。示例:
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
说明:
- 你可以假设所有的输入都是由小写字母
a-z
构成的。- 保证所有输入均为非空字符串。
解法一:
//时间复杂度O(n), 空间复杂度O(n), n是要处理的字符串长度
class Trie {
public:
/** Initialize your data structure here. */
Trie() : next(26, nullptr), end(false) {}
/** Inserts a word into the trie. */
void insert(string word) {
Trie* temp = this;
for(char c : word) {
if(!temp->next[c - 'a']) temp->next[c - 'a'] = new Trie;
temp = temp->next[c - 'a'];
}
temp->end = true;
}
/** Returns if the word is in the trie. */
bool search(string word) {
Trie* temp = this;
for(char c : word) {
if(!temp->next[c - 'a']) return false;
temp = temp->next[c - 'a'];
}
return temp->end;
}
/** Returns if there is any word in the trie that starts with the given prefix. */
bool startsWith(string prefix) {
Trie* temp = this;
for(char c : prefix) {
if(!temp->next[c - 'a']) return false;
temp = temp->next[c - 'a'];
}
return true;
}
private:
vector<Trie*> next;
bool end;
};
/**
* 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);
*/
解法一:
刚看到这题有些不知所云,直观上看用哈希表就OK。但是用哈希表有些问题:
- 在时间效率上,完全胜任insert和search操作,常数级时间需求。对于startwith操作,需要遍历整个哈希表查找所要求的前缀,低效。
- 在空间上,对于"apple"、"app"这样的字符串,并不能很好地利用具有相同前缀这个信息,导致空间效率也低。
所以需要找一个更高效的数组结构来表示它。还好已经有了成熟的解决方案,使用前缀树。
前缀树的每一个结点保存了两个信息:①该结点所拥有的字符;②该结点是否是字符串的结束字符。
node可以是这样:
struct Node {
char c;
vector<Node*> next;
bool end;
};
当然可以,其中next数组最多有26个元素(题上要求字符只能是小写字母),每次查找时检查c,再遍历next数组寻找下一个字符所在结点。但是该结构中next数组的顺序信息被浪费了,我们完全可以用它的下标来代替c,这样可以节省一丁点的空间,同时节省了检查下一个字符时对next检索的过程。可以改为如下的定义:
struct Node {
vector<Node*> next(26, nullptr);
bool end;
}
其他细节就很直观了,见代码。
2020/01/10 22:35