美文网首页
前缀树(Trie Tree)

前缀树(Trie Tree)

作者: 蓝笔头 | 来源:发表于2021-06-16 11:26 被阅读0次

背景

字典树,又称前缀树(英文名:Trie Tree),为 Edward Fredkin 发明。

举个例子,给出一些单词,(andasatcncom),则其字典树如下:

从上图可以发现,它有 3 个基本性质:

  • 1)根结点不包含字符,除根结点外每一个结点都只包含一个字符。
  • 2)从根结点到某一结点,路径上经过的字符连接起来,为该结点对应的字符串。
  • 3)每个结点的所有子结点包含的字符都不相同。

字典树是一个很重要的数据结构,其主要应用为:

  • 1)词频统计:例如,给定一个由 10 万个单词组成的库,现要你判断一个单词是否有在库中出现,若出现,求出共出现多少次。
  • 2)前缀匹配:以上图为例,如果想获取所有以 "a" 开头的字符串,那么从图中可以很明显的看到是:(andasat)。因此利用这个特性,可以巧妙地实现搜索提示功能,如输入一个网址,可以自动列出可能的选择。当没有完全匹配的搜索结果,可以列出前缀最相似的可能。

Java 实现

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class TrieTree {
    private Node root = new Node();

    public void putAll(String... words) {
        putAll(Arrays.asList(words));
    }

    public void putAll(List<String> words) {
        words.stream().forEach(this::put);
    }

    public void put(String word) {
        Node node = root;
        for (int i = 0; i < word.length(); i++) {
            char ch = word.charAt(i);
            Node next = node.addNextIfAbsent(ch);
            node = next;
        }
        node.markEnd();
    }

    public boolean contains(String text) {
        for (int i = 0; i < text.length(); ++ i) {
            if (contains(text, i, text.length())) {
                return true;
            }
        }
        return false;
    }

    private boolean contains(String word, int left, int right) {
        Node node = root;
        for (int i = left; i < right; i++) {
            char ch = word.charAt(i);
            Node next = node.findNext(ch);

            if (next == null) {
                return false;
            }
            if (next.isEnd()) {
                return true;
            }

            node = next;
        }
        return false;
    }

    private static class Node {
        private Map<Character, Node> children;
        private boolean end;

        public Node() {
            children = new HashMap<>();
            end = false;
        }

        public Node findNext(char ch) {
            return this.children.get(ch);
        }

        public Node addNextIfAbsent(char ch) {
            Node next = findNext(ch);
            if (next == null) {
                next = new Node();
                this.children.put(ch, next);
            }
            return next;
        }

        public boolean isEnd() {
            return this.end;
        }

        public void markEnd() {
            this.end = true;
        }
    }
}

测试

public class TrieDemo {
    public static void main(String[] args) {
        TrieTree trieTree = new TrieTree();
        trieTree.putAll("素人", "huhu");

        System.out.println(trieTree.contains("aaa素人bbb")); // true
        System.out.println(trieTree.contains("aaabbb")); // false
    }
}

复杂度分析

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

在实际的应用中,构建 trie 树的时间复杂度我觉得可以忽略,因为 trie 树我们可以在一开始就构建了,以后可以无数次重复利用的了。

参考

相关文章

网友评论

      本文标题:前缀树(Trie Tree)

      本文链接:https://www.haomeiwen.com/subject/fluxyltx.html