Skip to content

字典树

又称单词查找树,Trie树,是一种树形结构,是一种哈希树的变种。典型应用是用于统计,排序和保存大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。它的优点是:利用字符串的公共前缀来减少查询时间,最大限度地减少无谓的字符串比较,查询效率比哈希树高。

img

  • 3个基本性质:根节点不包含字符,除根节点外每一个节点都只包含一个字符; 从根节点到某一节点,路径上经过的字符连接起来,为该节点对应的字符串; 每个节点的所有子节点包含的字符都不相同。

  • 基本操作:查找、插入

  • 搜索字典项目的方法为:

(1) 从根结点开始一次搜索;

(2) 取得要查找关键词的第一个字母,并根据该字母选择对应的子树并转到该子树继续进行检索;

(3) 在相应的子树上,取得要查找关键词的第二个字母,并进一步选择对应的子树进行检索。

(4) 迭代过程……

(5) 在某个结点处,关键词的所有字母已被取出,则读取附在该结点上的信息,即完成查找。

其他操作类似处理

主要应用

串的快速检索

给出N个单词组成的熟词表,以及一篇全用小写英文书写的文章,请你按最早出现的顺序写出所有不在熟词表中的生词。

在这道题中,我们可以用数组枚举,用哈希,用字典树,先把熟词建一棵树,然后读入文章进行比较,这种方法效率是比较高的。

“串”排序

给定N个互不相同的仅由一个单词构成的英文名,让你将他们按字典序从小到大输出

用字典树进行排序,采用数组的方式创建字典树,这棵树的每个结点的所有儿子很显然地按照其字母大小排序。对这棵树进行先序遍历即可。

最长公共前缀

对所有串建立字典树,对于两个串的最长公共前缀的长度即他们所在的结点的公共祖先个数,于是,问题就转化为当时公共祖先问题。

JS实现和案例

// 字典树节点类
class Treenode {
  constructor(key, leaf) {
    this.key = key;
    this.leaf = leaf;
    this.count = 0;
    if (!leaf) {
      this.children = [];
    }
  }
}

// 字典树类(有问题)
class Tree{
  constructor() {
    let root = new Treenode(null, false);
    this.root = root;
  }

  // 写入一个单词
  run = (strs) => {
    let root = this.root;
    for (let i = 0; i < strs.length; i++) {
      this.insertNode(root, strs[i]);
    }
  }

  // 创建字典树子节点
  insertNode = (node, str) => {
    if (!node.leaf) {
      let child = node.children.find((child) => {
        return child.key === str[0];
      });
      if (child) {
        child.count++;
        // 这一句是递归的关键
        this.insertNode(child, str.substring(1));
      } else {
        let child = str.length === 1 ? new Treenode(str[0], true) : new Treenode(str[0], false);
        node.children.push(child);
        this.insertNode(child, str.substring);
      }
    }
  }

  // 搜索一个单词
  search = (strs) => {
    if (!strs) return null;
    return this.searchText(this.root, strs);
  }

  // 搜索单词
  searchText = (node, text) => {
    if (node.leaf) {
      return node.key === text[0] ? node : false;
    }
    else {
      let child = node.children.find(function(child) {
        return child.key === text[0];
      });
      if (!child) {
        return false;
      } else if (child.leaf) {
        return child;
      } else {
        return text.length === 1 ? false : this.searchText(child, substring(1));
      }
    }
  }

  // 获取字典树根节点 
  getRoot() {
    return this.root;
  }
}

var strs=['hello','hi','hello','hellen','red','read'];
var tree = new Tree();
tree.run(strs);
tree.search('hel');//false

案例2

class TrieNode {
  constructor(value) {
    this.value = value; //string
    this.num = 1;
    this.deep = 0;
    this.son = [];
    this.isEnd = false;
  }
  findNode(value) {
    for (let i = 0; i < this.son.length; i++) {
      const node = this.son[i];
      if (node.value == value) {
        return node;
      }
    }
    return null;
  }
}

class Trie {
  constructor() {
    this.root = new TrieNode(null);
    this.size = 1;
  }
  insert(str) {
    let node = this.root;
    for (let c of str) {
      let snode = node.findNode(c);
      if (snode == null) {
        snode = new TrieNode(c);
        snode.deep = node.deep + 1;
        node.son.push(snode);
      } else {
        snode.num++;
      }
      node = snode;
    }
    if (!node.isEnd) {
      this.size++;
      node.isEnd = true;
    }
  }
  has(str) {
    let node = this.root;
    for (let c of str) {
      const snode = node.findNode(c);
      if (snode) {
        node = snode;
      } else {
        return false;
      }
    }
    return node.isEnd;
  }
}

//demo
const nt=new Trie()
nt.insert('name');
nt.insert('word');
nt.insert('happy');
nt.insert('trie');

// console.log(nt.root['d'])
console.log(nt.has('has'))
console.log(nt.has('trie'))
console.log(nt.has('word'))

参考链接

https://baike.baidu.com/item/%E5%AD%97%E5%85%B8%E6%A0%91/9825209

https://www.cnblogs.com/panyujun/p/11098589.html

https://www.cnblogs.com/caoke/p/10828895.html


Last update: May 28, 2021