前缀树基本概念以及功能介绍

137 阅读4分钟

前言

本文介绍前缀树的基本概念以及代码实现前缀树的基本功能。

正文

前缀树又称PrefixTreeTrie Tree、字典树,

  • 在单个字符串中,字符从前到后的加到一棵多叉树上
  • 字符放在路上,节点上有专属的数据项(场景的是passend值)
  • 所有样本都这样添加,如果没有路就新建,如有路就复用
  • 沿途节点的pass值增加1,每个字符串结束时来到的节点end值增加1

可以完成前缀相关的查询

过程分析

假如有字符串组成的数组:["abc","abd","bce","abcd","bcf"],将这些字符串加入到一棵树里,加入过程如下:

遍历整个数组,对数组里每个字符串都进行遍历。

字符串"abc",先准备一个空节点,看这个节点有没有走向“a”的路,如果没有则新建一个节点,需要注意的是,字符不是放在节点上的,字符是放在路上的。然后依次创建走向b和c的路,如下

"abd",遍历第二个字符串,因为刚才已经创建了到a和到b的路,所以可以直接拿来复用,而b到d的路没有被创建过,所以只需要创建b到d的路即可,如图

同样的,字符串"abcd"和字符串"bcf"也按上述的步骤进行创建,如果路径已经被创建过直接复用,如果没有的话则进行新建,最终创建的路径图如下:

上面创建的前缀比较简单,节点都是空的,下面对节点做一些处理,给节点增加一些属性。

增加pass属性代表有多少路径经过了这个节点,增加end属性,代表这个节点是哪些路径的结尾。只有是有路径经过了某个节点,这个节点的pass属性就增加1,如果某个节点是某个路径的结束,那么end属性就增加1。

所以,增加pass和end属性后,上图路径结构就变为下图所示,方便起见pass使用p来表示,end使用e来表示。

经过上面分析之后,那么前缀树到底有什么用呢?

最简单的一个应用就是,从一个字符串集合中判断某个字符串出现过几次、或者有没有出现过。

例如在上图中,abcd这个字符串,从上图中按路径查找,找到d后的节点,p为1,e为1。说明abcd字符串出现过1次。再比如,abg这个字符串,在图中没有找到,说明该字符串没有出现过。

还可以用来判断,有多少字符串以某个字符串做前缀,例如,上图中有多少字符串以ab作为前缀呢?查找也很简单,也就是p的属性值,p是多少就代表有多少字符串是以这个作为前缀。

前缀树与哈希表相比,优势在于前缀树有可以找前缀的作用。

代码实现

下面以小写字符作为字符串字符,来看下前缀树的实现,首先来看下节点的定义:

public class Node {
    public int pass;
    public int end;
    public Node[] nexts;

    public Node() {
       pass = 0;
       end = 0;
       nexts = new Node[26];
    }
}
public class Trie {
   private Node root;

   public Trie() {
      root = new Node();
   }
}

Node的构造函数初始化了26个路径,因为都是小写字母组成的,所以最大也就有26个路径。

将字符串加入前缀树的方法实现:

public void insert(String word) {
   if (word == null) {
      return;
   }
   char[] str = word.toCharArray();
   Node node = root;
   node.pass++;
   int path = 0;
   for (int i = 0; i < str.length; i++) { 
    	// 由字符对应成走向哪条路,数组下标从0开始,所以减去a
      path = str[i] - 'a'; 
      if (node.nexts[path] == null) {
         node.nexts[path] = new Node();
      }
      node = node.nexts[path];
      node.pass++;
   }
   node.end++;
}

统计有多少以某个字符串作为前缀的数量:

public int prefixCount(String pre) {
   if (pre == null) {
      return 0;
   }
   char[] chs = pre.toCharArray();
   Node node = root;
   int index = 0;
   for (int i = 0; i < chs.length; i++) {
      index = chs[i] - 'a';
      if (node.nexts[index] == null) {
         return 0;
      }
      node = node.nexts[index];
   }
   return node.pass;
}

查找某个字符串出现了多少次:

public int search(String word) {
   if (word == null) {
      return 0;
   }
   char[] chs = word.toCharArray();
   Node node = root;
   int index = 0;
   for (int i = 0; i < chs.length; i++) {
      index = chs[i] - 'a';
      if (node.nexts[index] == null) {
         return 0;
      }
      node = node.nexts[index];
   }
   return node.end;
}

总结

本文介绍了前缀树的基本概念以及代码实现前缀树的基本功能:添加、搜索、前缀统计,本次只是介绍了前缀树的基本功能,其实前缀树还可以实现更复杂的功能。