Trie树学习--数据结构一发

Trie树

概念

Trie树,又称字典树,单词查找树或者前缀树,是一种用于快速检索的多叉树结构,如英文字母的字典树是一个26叉树,数字的字典树是一个10叉树。
Trie一词来自retrieve,发音为/tri:/ “tree”,也有人读为/traɪ/ “try”。
Trie树可以利用字符串的公共前缀来节约存储空间。如下图所示,该trie树用10个节点保存了6个字符串tea,ten,to,in,inn,int
《Trie树学习--数据结构一发》
在该trie树中,字符串in,inn和int的公共前缀是“in”,因此可以只存储一份“in”以节省空间。当然,如果系统中存在大量字符串且这些字符串基本没有公共前缀,则相应的trie树将非常消耗内存,这也是trie树的一个缺点。
Trie树的基本性质可以归纳为:
(1)根节点不包含字符,除根节点以外每个节点只包含一个字符。
(2)从根节点到某一个节点,路径上经过的字符连接起来,为该节点对应的字符串。
(3)每个节点的所有子节点包含的字符串不相同。

Trie树的应用

Trie是一种非常简单高效的数据结构, 有大量的应用实例
(1) 字符串检索
事先将已知的一些字符串(字典)的有关信息保存到trie树里,查找另外一些未知字符串是否出现过或者出现频率。
举例:
@ 给出N 个单词组成的熟词表,以及一篇全用小写英文书写的文章,请你按最早出现的顺序写出所有不在熟词表中的生词。
@ 给出一个词典,其中的单词为不良单词。单词均为小写字母。再给出一段文本,文本的每一行也由小写字母构成。判断文本中是否含有任何不良单词。例如,若rob是不良单词,那么文本problem含有不良单词。
(2)字符串最长公共前缀
Trie树利用多个字符串的公共前缀来节省存储空间,反之,当我们把大量字符串存储到一棵trie树上时,我们可以快速得到某些字符串的公共前缀。
举例:
@ 给出N 个小写英文字母串,以及Q 个询问,即询问某两个串的最长公共前缀的长度是多少?
解决方案:首先对所有的串建立其对应的字母树。此时发现,对于两个串的最长公共前缀的长度即它们所在结点的公共祖先个数,于是,问题就转化为了离线(Offline)的最近公共祖先(Least Common Ancestor,简称LCA)问题。
而最近公共祖先问题同样是一个经典问题,可以用下面几种方法:
1. 利用并查集(Disjoint Set),可以采用采用经典的Tarjan 算法;
2. 求出字母树的欧拉序列(Euler Sequence )后,就可以转为经典的最小值查询(Range Minimum Query,简称RMQ)问题了;
(关于并查集,Tarjan算法,RMQ问题,网上有很多资料。)
(3)排序
Trie树是一棵多叉树,只要先序遍历整棵树,输出相应的字符串便是按字典序排序的结果。
举例:
@ 给你N 个互不相同的仅由一个单词构成的英文名,让你将它们按字典序从小到大排序输出。
(4) 作为其他数据结构和算法的辅助结构
如后缀树,AC自动机等

Trie树复杂度分析

(1) 插入、查找的时间复杂度均为O(N),其中N为字符串长度。
(2) 空间复杂度是26^n级别的,非常庞大(可采用双数组实现改善)。

java代码实现

import org.apache.commons.lang3.StringUtils;

/** trie tree * Created by thushear */
public class Trie {

    private int SIZE = 26;//26个英文字母

    private TrieNode ROOT;//Trie树根节点

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

    private class TrieNode{

        private int num ;//通过该节点的单词树

        private TrieNode[] childs;//子节点

        private char value;//该节点的值

        private boolean isEnd;//是否最后

        public TrieNode() {
            isEnd = false;
            childs = new TrieNode[SIZE];
            num = 1;
        }
    }

    /** * 向根节点插入单词 * @param text * @return */
    public boolean insertTrieNode(String  text ){
        if (StringUtils.isEmpty(text)){
            return  false;
        }
        TrieNode node = ROOT;
        char[] chars = text.toCharArray();
        for (int i = 0; i < chars.length; i++) {
            int pos = chars[i] - 'a';
            if(node.childs[pos] == null){
                node.childs[pos] = new TrieNode();
                node.childs[pos].value = chars[i];
            }else {
                node.childs[pos].num ++;
            }
            node = node.childs[pos];
        }
        node.isEnd = true;
        return true;
    }

    /** * 计算当前树中某前缀开始的单词的数量 * @param prefix * @return */
    public int countPrefix(String prefix){
        if(StringUtils.isEmpty(prefix)){
            return -1;
        }
        TrieNode node = ROOT;
        char chars[] = prefix.toCharArray();
        for (int i = 0; i < chars.length; i++) {
            int pos = chars[i] - 'a';
            if(node.childs[pos] == null){
                return 0;
            }else {
                node = node.childs[pos];
            }
        }
        return node.num;
    }

    /** * 树结构是否精确匹配text * @param text * @return */
    public boolean  completeHasText(String text){
        if (StringUtils.isEmpty(text)) {
            return false;
        }
        TrieNode node = ROOT;
        char chars[] = text.toCharArray();
        for (int i = 0; i < chars.length; i++) {
            int pos = chars[i] - 'a';
            if(node.childs[pos]!=null){
                node = node.childs[pos];
            }else {
                return false;
            }
        }
        return node.isEnd;
    }


    public void iter(TrieNode node){
        if (node == null) {
            return;
        }

        System.out.print(node.value + "-");
        if (node.isEnd){
            System.out.println();
        }

        for (TrieNode child : node.childs) {

            iter(child);
        }
    }



    public static void main(String[] args) {
        Trie trie = new Trie();
        //向树节点插入数据
        trie.insertTrieNode("abcd");
        trie.insertTrieNode("abmn");
        trie.insertTrieNode("abop");
        trie.insertTrieNode("xyzm");
        trie.insertTrieNode("xyzo");
        trie.insertTrieNode("xylk");
        int count =  trie.countPrefix("ab");
        System.out.println("count = " + count);
        int count1 = trie.countPrefix("xyz");
        System.out.println("count1 = " + count1);

        System.out.println("hasText = " + trie.completeHasText("abcd"));
        System.out.println("hasText = " + trie.completeHasText("abc"));
        System.out.println("hasText = " + trie.completeHasText("xyzo"));

        trie.iter(trie.ROOT);
    }

}

http://baike.baidu.com/link?url=Hpnzo4lR2S0_asfQSoez-H6N938YlnSmtlouUlrIUKQ4DLHeQ82TH5euULBaTWm9Tnn4TbX261FnYa-gCtysHSSviEQIX92wQMoFZ3PiauGV91Lp25dM0OrbWkjvNfdvHO-j8FWkjMTa-7VpUad8za

http://dongxicheng.org/structure/trietree/

http://www.cnblogs.com/huangxincheng/archive/2012/11/25/2788268.html

    原文作者:Trie树
    原文地址: https://blog.csdn.net/lucas421634258/article/details/46738983
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞

发表评论

电子邮件地址不会被公开。 必填项已用*标注