前缀树的简单实现

1. 前缀树

前缀树又称为单词查找树,是一种树形的结构,用于存储大量的字符串,它的优点是:利用字符串的公共前缀来节约存储空间
Trie树主要是利用词的公共前缀缩小查词范围、通过状态间的映射关系避免了字符的遍历,从而达到高效检索的目的

2. 可以先声明一个节点TrieNode,节点包括以下几个属性:

TrieNode children[] = new TrieNode[26];
TrieNode parent;
int level;

//isLast未赋值的时候默认值是false
public boolean isLast;
public int fre = 1;

我们在插入的时候可以将字符串先转化为字符数组,然后逐个进行插入,一个字符下面可能有许多个相同的字符,所以声明TrieNode类数组是为了更好地表示上一个字符与下一个字符之间的关系,可以节省存储空间,而且我们在查询的时候知道当前的前缀那么就可以知道前缀后面的字符是什么了,isLast用来记录当前节点对应的字符是否是最后一个字符,fre用来计算当前节点对应的字符出现的次数

对于节点主要有插入和查询这两个操作,先插入后进行查询:

① 插入的时候可以传进需要插入的字符串,并将其转化为字符数组,通过循环将对应的类数组的位置标记一下(其实就是声明一个TrieNode节点赋值到这个类数组对应的位置),这里需要声明一个TrieNode类型的指针进行移动,方便存储下一个字符,这里的上一个字符与下一个字符是父子之间的关系,通过对象的引用将它们联系起来

②搜索的时候可以传进来需要搜索的字符串,先将其转化为字符数组,通过循环来查看当前字符对应的位置上是否有过标记,假如为空说明字典中不存在这个前缀直接退出函数即可,假如循环之后都不为空说明在插入的时候前缀是插入到字典中的,此时TrieNode指针指向前缀最后一个字符的类数组的节点

③在完成了②之后我们还可以进行打印操作,可以对插入的所有字符串进行打印出来,这个时候需要使用到dfs算法来进行打印,因为我们知道在插入的时候这些节点通过引用连接起来就是一棵树,通过dfs可以按照字典序的顺序将全部插入的字符串打印出来

此外因为在②中我们是将TrieNode指针指向前缀最后一个字符的类数组的节点,这个时候调用打印函数打印出前缀字符串对应的所有后缀,在输出函数中传入TrieNode指针,表示指向的是当前类数组中某一个的位置,通过dfs即可打印出全部的后缀

 

3. 代码如下:

①TrieNode代码:

public class TrieNode {
    TrieNode children[] = new TrieNode[26];
    TrieNode parent;
    int level;
    public boolean isLast;
    public int fre = 1;
}

Trie代码:

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

    public void insert(String str){
        char arr[] = str.toCharArray();
        TrieNode p = root;
        for(int i = 0; i < arr.length; i++){
            char c = arr[i];
            TrieNode child = p.children[c - 'a'];
            if(child == null){
                TrieNode nnode  = new TrieNode();
                nnode.level = i;
                p.children[c - 'a'] = nnode;
                p = nnode;
            }else{
                child.fre++;
                p = child;
            }            
        }
                p.isLast = true;
    }
    
    public void print(String prefix, TrieNode p){
        if(p.isLast && prefix.length() > 0){
            System.out.println(prefix + " " + p.fre);
        }
        for(int i = 0; i < 26; i++){
            if(p.children[i] != null){
                print(prefix + (char) ('a' + i), p.children[i]);
            }
        }
    }
    
    public void search(String prefix){
        char chars[] = prefix.toCharArray();
        TrieNode p = root;
        for(int i = 0; i < chars.length; i++){
            char c = chars[i];
            TrieNode child = p.children[c - 'a'];
            if(child == null){
                System.out.println("不存在");
                return;
            }else{
                p = child;
            }
        }
            print("", p);
    }
    
    public void printAll(){
        print("", root);
    }
}

③ Junited单元测试代码:

import org.junit.Before;
import org.junit.Test;
public class TrieTest {
    Trie trie = new Trie();
    @Test
    @Before
    public void insert() throws Exception {
        trie.insert("def");
        trie.insert("a");
        trie.insert("abcd");
        trie.insert("c");
        trie.insert("d");
    }
    //前缀为空的所有后缀
    @Test
    public void dfs() throws Exception{
        trie.printAll();
    }
    
    @Test
    public void search() throws Exception{
        trie.search("a");
        /*trie.search("h");*/
    }
}

 

你可能感兴趣的:(树)