题目来源:020711 每日1题:#676 实现一个魔法字典 ← #208 实现Trie(前缀树)
字典树基本描述
Trie/ Prefix Tree,一般指字典树或者前缀树。在有基本数据结构二叉树概念的基础上,简单理解为多叉树。以一般题目的26小写字母编码为例,即26叉树。
事实上,搭建Trie远不如构造一个AVL复杂;相比基本的二叉树,因为Trie的应用目标很明晰,所以操作花样也不必二叉树繁多。所以,有了基本数据结构的基础,理解为先,代码实现倒并不会更难。
字典树的实现
二维数组实现
我先是读了一种二维数组构建的代码,可能有的人会觉得,那种写法敲起来比较快;当然相应的代价,要做预分配数组的空间估计,而往往过估计会带来内存开销。好处按博文介绍是,相对标准实现形式,省去new每个新节点的时间开销。
推荐阅读:宫水三叶的字典树入门。
我是直接按顺序读的,先读代码会有点gap. 拉倒最后有些介绍其实挺清晰的了。我这里记录一个关键的点:index表观上记录了最新开辟的第n个结点编号;事实上,它有两层内涵:
1.因为是模拟树结构,每个结点时有后继的。编号为index的结点后继,将在trie[index][]这一行开辟新的空间。
2.也因此,index其实是二维数组大小的一个衡量。一般来说,先对二位数组估计;通常预留足够大,但如果遇到极端情形,也是可以通过index越界而预知,从而对数组空间重分配。
trieNode实现
我个人感觉敲起来也很快。
值得指出的一个点是,在单词插入字典完成后,需要在最后的节点上打上flag. 这个操作在判断前缀时是不需要的;但要做完全匹配,需要用它来判断单词完结。
class Trie {
public:
Trie() : isEnd(0), tns(26, nullptr) {
}
void insert(string word) {
Trie* node = this;
for (auto& i : word) {
if (node -> tns[i - 'a'] == nullptr) node -> tns[i - 'a'] = new Trie();
node = node->tns[i - 'a'];
}
node->isEnd = 1;
}
bool search(string word);
bool startsWith(string prefix);
private:
bool isEnd;
std::vector<Trie*> tns;
};