其他分享
首页 > 其他分享> > 【树】前缀树

【树】前缀树

作者:互联网

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

trie树常用于搜索提示。如当输入一个网址,可以自动搜索出可能的选择。当没有完全匹配的搜索结果,可以返回前缀最相似的可能。

举例:假如现在有 abc、adc、bc、bca、cde 4个字符串,构建的trie树如下

image-20220126205116223

前缀树的实现

//节点结构
public static class TrieNode {
    public int pass; //经过该节点的单词数量
    public int end; //以该节点为尾的单词数量
    /*
    当字符很多时,可以考虑
    HashMap<Character, TrieNode> nexts;
    或
    TreeMap<Character, TrieNode> nexts;
     */
    public TrieNode[] nexts;

    public TrieNode() {
        pass = 0;
        end = 0;
        //26个英文字母
        //nexts[0] != null 代表 a
        //nexts[1] != null 代表 b
        //nexts[2] != null 代表 c
        //nexts[3] != null 代表 d
        //nexts[4] != null 代表 e
        //……
        nexts = new TrieNode[26];
    }
}

下图基于该节点结构

image-20220126205825231
//前缀树的实现以及操作
public static class Trie {
    private TrieNode root; //根节点

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

    //插入word字符串
    public void insert(String word) {
        if (word == null) {
            return;
        }
        char[] chs = word.toCharArray();
        TrieNode node = root;
        node.pass++;
        int index = 0;
        for (char ch : chs) {
            index = ch - 'a'; //得到ch字符的位置
            if (node.nexts[index] == null) { //如果该字符为空就创建
                node.nexts[index] = new TrieNode(); 
            }
            node = node.nexts[index]; //取出ch字符所属节点
            node.pass++;
        }
        node.end++;
    }

    //word之前加入过几次
    public int search(String word) {
        if (word == null) {
            return 0;
        }
        char[] chs = word.toCharArray();
        TrieNode node = root;
        int index = 0;
        for (char ch : chs) {
            index = ch - 'a';//得到ch字符的位置
            if (node.nexts[index] == null) { //如果该位置为空表示word字串还没插入到前缀树中
                return 0;
            }
            node = node.nexts[index];
        }
        return node.end;
    }

    //所有加入的字符串中,有几个是以pre这个字符串作为前缀的
    public int prefixNumber(String pre) {
        if (pre == null) {
            return 0;
        }
        char[] chs = pre.toCharArray();
        TrieNode node = root;
        int index = 0;
        for (char ch : chs) {
            index = ch - 'a';//得到ch字符的位置
            if (node.nexts[index] == null) { //如果该位置为空表示前缀树中无以pre为前缀的字符串
                return 0;
            }
            node = node.nexts[index];
        }
        return node.pass;
    }

    //删除word
    public void delete(String word) {
        if (search(word) != 0) { //确定前缀树中确实加入过word,才删除
            char[] chs = word.toCharArray();
            TrieNode node = root;
            node.pass--;
            int index = 0;
            for (char ch : chs) {
                index = ch - 'a';//得到ch字符的位置
                //如果pass变为0表示前缀树中已经无从root到node.nexts[index]的字符串
                //需要释放node.nexts[index]的空间
                if (--node.nexts[index].pass == 0) {
                    node.nexts[index] = null;
                    return;
                }
                node = node.nexts[index];
            }
            node.end--;
        }
    }
}

标签:node,index,ch,word,前缀,nexts,null
来源: https://www.cnblogs.com/hzyuan/p/15848154.html