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 结构:

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;
};
posted @ 2020-11-17 14:13  浪荡&不羁  阅读(146)  评论(0编辑  收藏  举报