• Trie树


    参考:https://blog.csdn.net/johnny901114/article/details/80711441

    1.什么是Trie?

    Trie又称字典树,单词查找树。它的查找速度主要和字符串长度有关。

    Trie字典树主要用于存储字符串,Trie 的每个 Node 保存一个字符。用链表来描述的话,就是一个字符串就是一个链表。每个Node都保存了它的所有子节点。

    例如我们往字典树中插入see、pain、paint三个单词,Trie字典树如下所示:

    2.特点:

    1>共享字符串的公共前缀来节省空间。

    2>根节点root不存数据,每一整个分支代表一个字符串。

    3>每个分支的内部可能也含有完整的字符串,所以我们可以对于那些是某个字符串结尾的节点做一个标记(isWord=true/false)。

    3.实现:

      1 /**
      2  * 
      3  */
      4 package trie;
      5 
      6 import java.util.HashMap;
      7 import java.util.Map;
      8 
      9 /**
     10  * @author Administrator
     11  * 
     12  */
     13 public class Trie {
     14     private Node root;
     15     private int size;//单词的个数
     16     
     17     private class Node{
     18         public boolean isWord;
     19         public Map<Character,Node> next;
     20         
     21         public Node(){
     22             next=new HashMap<>();
     23         }
     24         
     25         public Node(boolean isWord){
     26             this();
     27             this.isWord=isWord;
     28         }
     29     }
     30     
     31     public Trie(){
     32         root = new Node();
     33     }
     34     public int getSize(){//获取
     35         return size;
     36     }
     37     public boolean isEmpty(){
     38         return size==0;
     39     }
     40     /**
     41      * 往Trie添加一个单词
     42      * @param word 单词
     43      */
     44     public void add(String word){
     45         Node current=root;
     46         char[] charArray = word.toCharArray();
     47         for (char c : charArray) {
     48             Node next = current.next.get(c);
     49             if(next==null){
     50                 current.next.put(c, next);
     51             }
     52             current=next;
     53         }
     54         if(!current.isWord){//如果当前node已经是一个word,则跳过
     55             size++;
     56             current.isWord=true;
     57         }
     58     }
     59     /**
     60      * 判断Trie中是否包含单词
     61      * @param word
     62      * @return true/false
     63      */
     64     public boolean contains(String word){
     65         Node current = root;
     66         char[] charArray = word.toCharArray();
     67         for (char c : charArray) {
     68             Node next = current.next.get(c);
     69             if(next==null){
     70                 return false;
     71             }
     72             current=next;
     73         }
     74         return current.isWord;
     75     }
     76     /**
     77      * 判断Trie中是否包含前缀
     78      * @param prefix
     79      * @return true/false
     80      */
     81     public boolean containsPrefix(String prefix){
     82         Node current = root;
     83         char[] charArray = prefix.toCharArray();
     84         for (char c : charArray) {
     85             Node next = current.next.get(c);
     86             if(next==null){
     87                 return false;
     88             }
     89             current=next;
     90         }
     91         return true;
     92     }
     93     /**
     94      * 删除一个单词
     95      * 1:如果单词的所有字母的都没有多个分支,删除整个单词
     96      * 2:如果单词是另一个单词的前缀,只需要把该word的最后一个节点的isWord的改成false
     97      * 3:如果单词的除了最后一个字母,其他的字母有多个分支
     98      * @param word
     99      * @return true/false
    100      */
    101     public boolean remove(String word){
    102         Node current = root;
    103         Node multiChildNode = null;
    104         int multiChildNodeIndex = -1;
    105         char[] charArray = word.toCharArray();
    106         for (int i=0; i<charArray.length; i++) {
    107             Node child = current.next.get(charArray[i]);
    108             //如果Trie中不存在该单词
    109             if(child==null){
    110                 return false;
    111             }
    112             //如果当前节点的子节点个数大于1个
    113             if(child.next.size() >1 ){
    114                 multiChildNode=child;
    115                 multiChildNodeIndex=i;
    116             }
    117             current=child;
    118         }
    119         //情况1:单词的所有字母都没有多余分支(分支都只有1个),则删除整个单词
    120         if(multiChildNodeIndex == -1){
    121             root.next.remove(charArray[0]);
    122             size--;
    123             return true;
    124         }
    125         //情况2:如果单词后面还有子节点
    126         if(current.next.size() > 0){
    127             //如果当前节点的isWord为true
    128             if(current.isWord){
    129                 current.isWord=false;
    130                 size--;
    131                 return true;
    132             }
    133             //否则,不存在该单词,该单词只是前缀
    134             return false;
    135         }
    136         //情况3:如果单词的字母除了最后一个没有分支,其他字母存在分支
    137         if(multiChildNodeIndex != charArray.length-1){//multiChildNodeIndex == -1 已经做过判断,这里 multiChildNodeIndex!=-1
    138             multiChildNode.next .remove(charArray[multiChildNodeIndex+1]);
    139             size--;
    140             return true;
    141         }
    142         return false;
    143     }
    144 }

    4.应用:

    1>Trie 最大的特点就是利用了字符串的公共前缀,像我们有时候在百度、谷歌输入某个关键字的时候,它会给我们列举出很多相关的信息

    这种就是通过Trie实现的。

    2>Trie 树来实现敏感词过滤

    5.时间复杂度:

    如果敏感词的长度为 m,则每个敏感词的查找时间复杂度是 O(m),字符串的长度为 n,我们需要遍历 n 遍,所以敏感词查找这个过程的时间复杂度是 O(n * m)。

    如果有 t 个敏感词的话,构建 trie 树的时间复杂度是 O(t * m)。

  • 相关阅读:
    039 RabbitMq及数据同步01
    038 商品详情02-----页面静态化
    037 商品详情01
    036 搭建搜索微服务04----分类和品牌的过滤
    035 搭建搜索微服务03----页面分页效果
    034 通过域名访问服务器或本地的图片资源---switchhost+nginx
    033 搭建搜索微服务02----实现基本搜索功能
    ubuntu server 无线网卡的处理
    grub 启动错误 "file not found"
    Ubuntu 的 desktop 和 server 还是有区别。
  • 原文地址:https://www.cnblogs.com/xc-chejj/p/10839658.html
Copyright © 2020-2023  润新知