zoukankan      html  css  js  c++  java
  • 数据结构 -- Trie字典树

    简介

      字典树:又称单词查找树,Trie树,是一种树形结构,是一种哈希树的变种。

      优点利用字符串的公共前缀来减少查询时间,最大限度地减少无谓的字符串比较,查询效率比哈希树高。

      性质:   1.  根节点不包含字符,除根节点外每一个节点都只包含一个字符;

          2. 从根节点到某一节点,路径上经过的字符连接起来,为该节点对应的字符串;

          3.  每个节点的所有子节点包含的字符都不相同。

      应用场景:用于统计,排序保存大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。

          

    代码 实现

     本文使用链表来实现Trie字典树,字符串的每个字符作为一个Node节点,Node主要有两部分组成:

    1. 是否是单词 (boolean isWord)
    2. 节点所有的子节点,用map来保存 (Map next)

    添加

    public void add(String word) {
        Node current = root;
        char[] cs = word.toCharArray();
        for (char c : cs) {
            Node next = current.next.get(c);
            if (next == null) {
                //一个字符对应一个Node节点
                current.next.put(c, new Node());
            }
            current = current.next.get(c);
        }
        //current就是word的最后一个字符的Node
        
        //如果当前的node已经是一个word,则不需要添加
        if (!current.isWord) {
            size++;
            current.isWord = true;
        }
    }

    查找

      Trie查找操作就比较简单了,遍历带查找的字符串的字符,如果每个节点都存在,并且待查找字符串的最后一个字符对应的Node的 isWord 属性为 true ,则表示该单词存在

    public boolean contains(String word) {
        Node current = root;
        for (int i = 0; i < word.length(); i++) {
            char c = word.charAt(i);
            Node node = current.next.get(c);
            if (node == null) {
                return false;
            }
            current = node;
        }
        //current就是word的最后一个字符的Node
        return current.isWord;
    }

    前缀查询

    public boolean containsPrefix(String prefix) {
        Node current = root;
        for (int i = 0; i < prefix.length(); i++) {
            char c = prefix.charAt(i);
            Node node = current.next.get(c);
            if (node == null) {
                return false;
            }
            current = node;
        }
        return true;
    }

    删除

    Trie的删除操作就稍微复杂一些,主要分为以下3种情况:

      1. 如果单词是另一个单词的前缀

        如果待删除的单词是另一个单词的前缀,只需要把该单词的最后一个节点的 isWord 的改成false,比如Trie中存在 panda 和 pan 这两个单词,删除 pan ,只需要把字符 n 对应的节点的 isWord 改成 false 即可。

          

      2. 如果单词的所有字母的都无分支,删除整个单词。

        如果单词的所有字母的都没有多个分支(也就是说该单词所有的字符对应的Node都只有一个子节点),则删除整个单词。      

          

      3. 如果单词的除了最后一个字母,其他的字母有多个分支

        

     public boolean remove(String word){
            Node multiChildNode = null;
            int multiChildNodeIndex = -1;
            Node current = root;
            for (int i = 0; i < word.length(); i++) {
                Node child = current.next.get(word.charAt(i));
                //如果Trie中没有这个单词
                if (child == null) {
                    return false;
                }
                //当前节点的子节点大于1个
                if (child.next.size() > 1) {
                    multiChildNodeIndex = i;
                    multiChildNode = child;
                }
                current = child;
            }
            //如果单词后面还有子节点
            if (current.next.size() > 0) {
                if (current.isWord) {
                    current.isWord = false;
                    size--;
                    return true;
                }
                //不存在该单词,该单词只是前缀
                return false;
            }
            //如果单词的所有字母的都没有多个分支,删除整个单词
            if (multiChildNodeIndex == -1) {
                root.next.remove(word.charAt(0));
                size--;
                return true;
            }
            //如果单词的除了最后一个字母,其他的字母有分支
            if (multiChildNodeIndex != word.length() - 1) {
                multiChildNode.next.remove(word.charAt(multiChildNodeIndex + 1));
                size--;
                return true;
            }
            return false;
        }

    Trie查询效率非常高,但是对空间的消耗还是挺大的,这也是典型的空间换时间。

    可以使用 压缩字典树(Compressed Trie) ,但是维护相对来说复杂一些。

    如果我们不止存储英文单词,还有其他特殊字符,那么维护子节点的集合可能会更多。

    可以对Trie字典树做些限制,比如每个节点只能有3个子节点,左边的节点是小于父节点的,中间的节点是等于父节点的,右边的子节点是大于父节点的,这就是三分搜索Trie字典树(Ternary Search Trie)。

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

  • 相关阅读:
    2018 南京网络预赛Sum
    一个莫比乌斯等式的证明
    LOJ 2452 对称 Antisymmetry——用hash求回文串数
    LOJ 103子串查找——用hash代替kmp算法
    LOJ2823 三个朋友 ——查询字串的哈希值
    hash入门
    2019牛客暑期多校训练营(第十场)Coffee Chicken——递归
    2019牛客暑期多校训练营(第十场)Han Xin and His Troops——扩展中国剩余定理
    mutex 的 可重入
    Linux 编译安装Boost
  • 原文地址:https://www.cnblogs.com/FondWang/p/11896425.html
Copyright © 2011-2022 走看看