概念
字典树,是一种有序树,用于保存关联数组,其中的键通常是字符串。与二叉查找树不同,键不是直接保存在节点中,而是由节点在树中的位置决定。一个节点的所有子孙都有相同的前缀,也就是这个节点对应的字符串,而根节点对应空字符串。一般情况下,不是所有的节点都有对应的值,只有叶子节点和部分内部节点所对应的键才有相关的值。
实现
字典树节点
1class TrieNode {2 constructor(key) {3 this.key = key;4 this.children = [];5 }6}
字典树
1class Trie {2 constructor() {3 this.root = new TrieNode(null);4 }56 /** 插入 */7 insert(string) {8 this._insert(string, this.root);9 }1011 /**12 * 插入数据到指定节点13 * @param {string} string 数据14 * @param {TrieNode} node 操作节点15 */16 _insert(string, node) {17 if (!string) {18 return;19 }20 let children = node.children;21 let header;22 // 遍历子节点,存在与第一个字符相等的节点赋值为 header23 children.map(child => {24 if (child.key === string[0]) {25 header = child;26 }27 })28 if (header) {29 // 递归:除了第一个字符外的其他字符插入30 this._insert(string.substring(1), header);31 } else {32 let node = new TrieNode(string[0])33 if (!children.length) {34 // 操作节点无子节点时,直接将操作节点push到子节点中35 children.push(node);36 } else {37 // 否则,对比操作节点与子节点的ASCII码,按序插入到对应位置38 let position = 0;39 children.map(child => {40 if (child.key < string[0]) {41 position++;42 }43 })44 children.splice(position, 0, node);45 }46 this._insert(string.substring(1), node);47 }48 }4950 /** 查找树中是否有string */51 search(string) {52 if (string === '' || !this.root.children.length) {53 return false;54 }55 for (let i = 0; i < this.root.children.length; i++) {56 if (this._search(string, this.root.children[i])) {57 return true;58 }59 }60 return false;61 }6263 /**64 * 查找某数据是否在指定节点中65 * @param {string} string66 * @param {TrieNode} node67 */68 _search(string, node) {69 if (node.key !== string[0]) {70 return false;71 }72 let children = node.children;73 // 无子节点的key值与string相同74 if (!children.length && string.length === 1) {75 return true;76 }77 if (children.length > 0 && string.length > 1) {78 // 递归查询79 for (let i = 0; i < children.length; i++) {80 if (children[i].key === string[1]) {81 return this._search(string.substring(1), children[i]);82 }83 }84 } else {85 return false;86 }8788 }8990 delete(string) {91 if (this.search(string)) {92 for (let i = 0; i < this.root.children.length; i++) {93 if (this.root.children[i].key === string[0]) {94 this._delete(string, this.root, i, string)95 return true;96 }97 }98 } else {99 return false;100 }101 }102103 /**104 * @param {string} string105 * @param {TrieNode} parent106 * @param {number} index107 * @param {string} originString108 */109 _delete(string, parent, index, originString) {110 // key值与string[0]相等的节点111 let node = parent.children[index];112 let children = node.children;113 if (!children.length && string.length === 1) {114 // string和node.key相等 且 node无子节点115 parent.children.splice(index, 1);116 return this.delete(originString.substring(0, originString.length - 1))117 // this.delete(delStr.substring(0, delStr.length - 1))118 } else if (children.length && string.length > 1) {119 for (let i = 0; i < children.length; i++) {120 if (children[i].key === string[1]) {121 this._delete(string.substring(1), node, i, originString);122 return true123 }124 }125 }126 return false;127 }128129 show() {130 this._show(this.root);131 }132133 _show(node) {134 node.children.map(child => {135 console.log('parent: ' + node.key + '; node:', child);136 if (child.children.length) {137 this._show(child);138 }139 })140 }141}
示例
1const tree = new Trie();2tree.insert('girl');3tree.insert('boy');4tree.insert('book');5tree.insert('go');67console.log(tree.search('b')); // false8console.log(tree.search('boy')); // true910tree.delete('go')1112console.log(tree.root);13tree.show();