Trie(prefix tree)
Trie
Trie 是一种特殊的数据结构,与二叉树类似,只是 Trie 不限子孩子数量。
Trie 又名字典树,单词查找树,前缀树。我们可以使用 Trie 来构造工作中使用到的 红点系统 。
下面以 LeetCode 的第208题 Implement Trie (Prefix Tree) 来讨论实现 Trie 这种特殊的数据结构。
题目描述
Implement a trie with insert, search, and startsWith methods.
Trie trie = new Trie();
trie.insert("apple");
trie.search("apple"); // returns true
trie.search("app"); // returns false
trie.startsWith("app"); // returns true
trie.insert("app");
trie.search("app"); // returns true
Note: You may assume that all inputs are consist of lowercase letters a-z.All inputs are guaranteed to be non-empty strings.
实现 Trie
假设我们要往 Trie 中加入 ['apple', 'app', 'about', 'bed']。
在创建 Trie 时,我们以对象 Object 为存储类型,对一个单词字母都是对象的一个属性,然后在每个单词末尾时,我们加多一个单词结尾标识 isEnd。
把对应的单词插入 Trie 后,我们将得到下图所示的 Trie 结构:
1. 插入单词
插入单词时,每一个字母对应一个结点对象,如果没有对应的结点则创建;如果有就在此结点下继续查找。在单词的结尾时,我们需加入单词结束标识 isEnd,以便查找。
let obj = this.root;
for (let i = 0; i < word.length; i++) {
let c = word[i];
// 没有这个字母结点,创建
if (!obj[c]) {
obj[c] = {};
}
obj = obj[c];
}
// 标识单词结尾
obj.isEnd = true;
2. 查找单词或前缀
在查找单词或前缀的时候,需要一步一步的往下查找,如果中途没有任一个结点,则直接返回即可。如果是查找单词,找到了对应的最后一个字母结点后,我们仍需判断结点是否有 isEnd 属性;如果是前缀,则判断是否是一个对象即可。
// 查找函数
Trie.prototype.commonSearch = function (word) {
let obj = this.root;
for (let char of word.split('')) {
if (!obj[char]) {
// 对应对应字母的结点,返回null
return null;
} else {
obj = obj[char];
}
}
// 找到最后一个单词结点,返回结点对象
return obj;
};
完整代码附下
/**
* Initialize your data structure here.
*/
const Trie = function () {
this.root = {};
};
/**
* Inserts a word into the trie.
* @param {string} word
* @return {void}
*/
Trie.prototype.insert = function (word) {
let obj = this.root;
for (let i = 0; i < word.length; i++) {
let c = word[i];
if (!obj[c]) {
obj[c] = {};
}
obj = obj[c];
}
obj.isEnd = true;
};
/**
* Returns if the word is in the trie.
* @param {string} word
* @return {boolean}
*/
Trie.prototype.search = function (word) {
let obj = this.commonSearch(word);
return obj && obj.isEnd ? true : false;
};
/**
* Returns if there is any word in the trie that starts with the given prefix.
* @param {string} prefix
* @return {boolean}
*/
Trie.prototype.startsWith = function (prefix) {
let obj = this.commonSearch(prefix);
return !!obj;
};
/**
* @param {string} prefix
* @return {any}
*/
Trie.prototype.commonSearch = function (word) {
let obj = this.root;
for (let char of word.split('')) {
if (!obj[char]) {
return null;
} else {
obj = obj[char];
}
}
return obj;
};