字典树(java实现)

一、什么是java字典树?
Trie树,即字典树,又称单词查找树或键树,是一种树形结构,是一种哈希树的变种。典型应用是用于统计和排序大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。它的优点是:最大限度地减少无谓的字符串比较,查询效率比哈希表高。
Trie的核心思想是空间换时间。利用字符串的公共前缀来降低查询时间的开销以达到提高效率的目的。
它有3个基本性质:
1)根节点不包含字符,除根节点外每一个节点都只包含一个字符。
2)从根节点到某一节点,路径上经过的字符连接起来,为该节点对应的字符串。
3)每个节点的所有子节点包含的字符都不相同。
二、字典树的应用
1)查询
Trie树是简单但实用的数据结构,通常用于实现字典查询。我们做即时响应用户输入的AJAX搜索框时,就是Trie开始。本质上,Trie是一颗存储多个字符串的树。相邻节点间的边代表一个字符,这样树的每条分支代表一则子串,而树的叶节点则代表完整的字符串。和普通树不同的地方是,相同的字符串前缀共享同一条分支。下面,再举一个例子。给出一组单词,inn, int, at, age, adv, ant, 我们可以得到下面的Trie:
这里写图片描述
每条边对应一个字母。
每个节点对应一项前缀。叶节点对应最长前缀,即单词本身。
单词inn与单词int有共同的前缀“in”, 因此他们共享左边的一条分支,root->i->in。同理,ate, age, adv, 和ant共享前缀”a”,所以他们共享从根节点到节点”a”的边。
每条边对应一个字母。
每个节点对应一项前缀。叶节点对应最长前缀,即单词本身。
单词inn与单词int有共同的前缀“in”, 因此他们共享左边的一条分支,root->i->in。同理,ate, age, adv, 和ant共享前缀”a”,所以他们共享从根节点到节点”a”的边。
查询操纵非常简单。比如要查找int,顺着路径i -> in -> int就找到了。

搭建Trie的基本算法也很简单,无非是逐一把每则单词的每个字母插入Trie。插入前先看前缀是否存在。如果存在,就共享,否则创建对应的节点和边。比如要插入单词add,就有下面几步:

1.考察前缀”a”,发现边a已经存在。于是顺着边a走到节点a。
2.考察剩下的字符串”dd”的前缀”d”,发现从节点a出发,已经有边d存在。于是顺着边d走到节点ad
3.考察最后一个字符”d”,这下从节点ad出发没有边d了,于是创建节点ad的子节点add,并把边ad->add标记为d。
2)一个文本文件,大约有一万行,每行一个词,要求统计出其中最频繁出现的前10个词,请给出思想,给出时间复杂度分析.
先用trie树统计每个词出现的次数,时间复杂度是O(n*le)(le表示单词的平均长度);
然后是用小顶堆找出出现最频繁的前10个词,时间复杂度是O(n*lg10)。
3)1000万字符串,其中有些是相同的(重复),需要把重复的全部去掉,保留没有重复的字符串。
使用hash_map或者trie树。
比如trie树,在构建trie树的过程中,如果某个字符串已经存在于trie中则不输出,否则输出到文本中,这样就可以得到不重复的字符串。
hash_map的速度会要快一些,因为在添加一个字符串的时候,hashmap直接用哈希函数就能定位,然后选择是否写入文件,但是trie树需要在子节点中比较。
trie树对hashmap的优势是,在大量重复的单词中,trie树需要的内存会低一些。
三、字典树的实现

/**
 * 字典树的节点类型
 */
class TrieNode{
    char ch;
    int freqs;  //记录单词出现次数
    Map<Character, TrieNode> nodeMap;

    public TrieNode(char ch, int freqs, Map<Character, TrieNode> nodeMap) {
        this.ch = ch;
        this.freqs = freqs;
        this.nodeMap = nodeMap;
    }
}

/**
 * 描述: 字典树的实现
 *
 */
public class TrieTree {
    private TrieNode root;

    public TrieTree(){
        root = new TrieNode('\u0000', 0, new HashMap<Character, TrieNode>());
    }

    /**
     * 查询str字符串是否存在,不存在返回0,存在返回该字符串的个数
     * @param str
     * @return
     */
    public int query(String str){
        TrieNode cur=root;
        if(root==null){
            return 0;
        }
        for(int i=0;i<str.length();i++){
            TrieNode child=cur.nodeMap.get(str.charAt(i));
            if(child==null){
                return 0;
            }else {
                cur = child;
            }
        }
        return cur.freqs;
    }
/**
*添加单词
*/
    public void add(String str){
        TrieNode cur=root;
        if(root==null){
            return;
        }
        for(int i=0;i<str.length();i++){
            TrieNode child=cur.nodeMap.get(str.charAt(i));
            if(child==null){
                TrieNode node=new TrieNode(str.charAt(i),0,new HashMap<Character, TrieNode>());
                cur.nodeMap.put(str.charAt(i),node);
                cur=cur.nodeMap.get(str.charAt(i));
            }else{
                cur=child;
            }
        }
       cur.freqs++;
    }

    /**
     * 判断str是否存在
     * @param str
     * @return
     */

    public boolean search(String str){
        TrieNode cur=root;
        if(root==null){
            return false;
        }
        for(int i=0;i<str.length();i++){
            TrieNode child=cur.nodeMap.get(str.charAt(i));
            if(child==null){
                return false;
            }else {
                cur = child;
            }
        }
        return true;
    }


    /**
     * 删除str字符串
     * 三种情况
     *
     */
    public void remove(String str) {
        if(search(str)==false){
            return;
        }
        TrieNode cur=root;
        TrieNode delPreNode=root;
        char delch=str.charAt(0);
        for(int i=0;i<str.length();i++){
            TrieNode child=cur.nodeMap.get(str.charAt(i));
            if(child.nodeMap==null){  //后面没有结点
              return;
            }else if(i<str.length()-1 && (child.nodeMap.get(str.charAt(i+1))!=null) && child.nodeMap.size()>0){
                delPreNode=child;
                delch=str.charAt(i+1);
            }
            cur=child;
        }
        if(cur.nodeMap.size()>0){
            cur.freqs=0;
        }else{
            cur.nodeMap.remove(delch);
        }
    }
}

猜你喜欢

转载自blog.csdn.net/xd_fybdw/article/details/81149742
今日推荐