02.字典树orTrie树or前缀树-leetcode208,211

一.基本概念

1.Trie树,即字典树或者前缀树,又称单词查找树或键树,是一种树形结构,是一种哈希树的变种,前缀树每个节点存字母,路径代表单词。
2.Trie的核心思想是空间换时间。利用字符串的公共前缀来降低查询时间的开销以达到提高效率的目的。
3.通常字典树的查询时间复杂度是O(logL),L是字符串的长度。
4.字典树保存了串的公共前缀信息,因此可以降低查询操作的复杂度。以英文单词构建的字典树为例,这棵Trie树中每个结点包括26个孩子结点,因为总共有26个英文字母(假设单词都是小写字母组成)。
5.举例:
下面橙色的是一个单词的结束。root不存储信息,可以看出这个数存储的单词前缀相同的在一条线上,所以如果
只算小写英文字符,字典树中每个节点的孩子节点最多有26个。
下面组成的单词为:abc,bac,bbc,ca.
在这里插入图片描述

二.建立字典树

在这里插入图片描述
定义一个节点,每个节点包含一个节点数组,代表 26 个孩子。此外,还需要一个 flag 变量来标记当前节点是否是某个单词的结束。
每个节点它下面都可能包含26个可能的孩子,先将这些孩子列表设置为空。

class TrieNode {
    TrieNode[] children;
    boolean flag;
    public TrieNode() {
        children = new TrieNode[26];
        flag = false;
        //节点初始化为 null,说明此时它没有孩子节点,只有当此处的孩子节点添加进TrieNode才是表示当前位置对应的字母存在
        for (int i = 0; i < 26; i++) {
            children[i] = null;
        }
    }
}
其中 children[0] 存 a, children[1] 存 b, children[2] 存 c... 依次类推。
所以存的时候我们用当前字符减去 a ,从而得到相应的 children 下标。
所谓 children[0] 存a实际上只是将此处节点设置为存在而已,就是新建了一个TrieNode填在对应的children列表就是这个位置对应的字符存在。
比如children[1]如果是null的话,说明孩子节点没有'b',如果是存在TrieNode就是存在'b'这个孩子节点,以相应的位置对应字母。
class Trie {
    class TrieNode{
        TrieNode[] children; 
        boolean flag = false;
        public TrieNode(){
            children = new TrieNode[26];
            for(int i = 0;i<children.length;i++){
                children[i] = null;
            }
        }
    }
    TrieNode root;
    /** Initialize your data structure here. */
    public Trie() {
        //root不存任何信息,只是初始化下
        root = new TrieNode();
    }
    
    /** Inserts a word into the trie.
    思路:就是根据前缀字符节点一个个往下找,如果没有这个字符就创建
     */
    public void insert(String word) {
        char[] dic = word.toCharArray();
        //刚开始的时候把root作为起始节点,在调用trie的时候自动新建 root = new TrieNode()对象
        TrieNode cur = root;
        for(int i= 0;i<dic.length;i++){
            //dic[i]是word的一个字符,dic[i]-'a'是这个字符应该存储的children的位置,如果cur中当前此位置为空,那么就是之前就没有,就要新建,建立后这个children就不是空了,就说明此时有这个相应字符的孩子节点
            if(cur.children[dic[i]-'a']==null){
                cur.children[dic[i]-'a'] = new TrieNode();
            }
            //让cur当前节点指向  cur的孩子节点(这个孩子节点是和上面单词遍历的字符对应的)
            cur = cur.children[dic[i]-'a'];
        }
        //把最后的节点的flag标记为true,以表示是结束位置
        cur.flag = true;
    }
    
    /** Returns if the word is in the trie. 
         思路:就是不断随着字符往下找,找不到了就返回false,找的到就返回最后cur的flag,看这个是否是结尾符号,true就是结尾符号,false就不是。在插入的时候也是这样设计的
    */
    public boolean search(String word) {
        TrieNode cur = root; 
        char[] dic  = word.toCharArray();
        for(int i = 0;i<dic.length;i++){
            if(cur.children[dic[i]-'a']==null){
                return false;
            }
            cur = cur.children[dic[i]-'a'];
        }
        return cur.flag;
    }
    
    /** Returns if there is any word in the trie that starts with the given prefix. */
    public boolean startsWith(String prefix) {
        TrieNode cur = root; 
        char[] dic = prefix.toCharArray();
        for(int i= 0;i<dic.length;i++){
            if(cur.children[dic[i]-'a']==null){
                return false;
            }
            cur = cur.children[dic[i]-'a'];
        }
        return true;
    }
}
/**
 * Your Trie object will be instantiated and called as such:
 * Trie obj = new Trie();
 * obj.insert(word);
 * boolean param_2 = obj.search(word);
 * boolean param_3 = obj.startsWith(prefix);
 */

三.添加与搜索单词 - 数据结构设计

在这里插入图片描述

class WordDictionary {
    class TrieNode{
        TrieNode[] children;
        boolean flag=false;
        public TrieNode(){
             children = new TrieNode[26];
             for(int i =0;i<children.length;i++){
                 children[i] = null;
            }
        }
    }
    TrieNode root;
    /** Initialize your data structure here. */
    public WordDictionary() {
        root = new TrieNode();
    }
    
    /** Adds a word into the data structure.
	思路和208一样
	 */
    public void addWord(String word) {
        TrieNode cur = root; 
        char[] dic = word.toCharArray();
        for(int i=0;i<word.length();i++){
            if(cur.children[dic[i]-'a']==null){
                cur.children[dic[i]-'a'] = new TrieNode();
            }
            cur = cur.children[dic[i]-'a'];
        }
        cur.flag = true;
    }
    
    /** Returns if the word is in the data structure. A word could contain the dot character '.' to represent any one letter.
	思路:对于普通的a-z字符用普通的算法,就是判断在不在当前节点的孩子节点中(就是当前字符对应的位置上,这个节点对应的此处位置是否为空),空的话证明
	字典树中没有,直接返回false,有的话判断最后的节点的flag是否为true。
	而对于字符“.”,则要注意要尝试这个字符节点的最多26种可能(只要当前对应的字典树的位置不为空都行),但是这最多26种可能又包含了无数的下一步可能。所以只要“.”节点的下一个字符在字典树中对应存在,
	就把单词从“.”下一个字符到最后一个算作新的word,而在字典树上的当前节点(用来替代“.”的字符的节点)的孩子节点只要不为空,就算作是新的头节点,反正头节点肯定是匹配上了,所以在新的递归中就相当于空信息
	
	 */
    public boolean search(String word) {
        TrieNode node = root;
        return searchFinal(node,word); 
    }
    public boolean searchFinal(TrieNode root,String word){
        char[] dic = word.toCharArray();
        TrieNode cur = root;
        //遍历单词字符
        for(int i = 0;i<dic.length;i++){
            //如果有一个字符是“.”,由于它可以表示任何字符,所以就以这个字符的所有不为空的孩子节点往下去递归着走一遍,看看每种结果是否有可能。
            //所以下面这个j到26,因为他要尝试每种可能
            if(dic[i]=='.'){
                for(int j=0;j<26;j++){
                    //“.”字符节点的第j个孩子节点不为空,就往下走试试
                    if(cur.children[j]!=null){
                        //往下走就是把"."的下一个字符到最后认为是一个新的字符串,做递归,比如'ab.cd'到了‘.’就把‘cd’作为新的字符串往下找,如果在字典树上找到了且符合条件,那么最后返回就是true,就会进入if返回true.
                        if(searchFinal(cur.children[j],word.substring(i+1))){
                            return true;
                        }
                    }
                  
                }
                return false;
            }

            //如果不含有某个字符则直接返回false
            if(cur.children[dic[i]-'a']==null){
                return false;
            }
            cur = cur.children[dic[i]-'a'];
        }
        //判断这个字符是否是结尾字符,如果最后字符是.就会在上面就结束返回了,不会走到这
        return cur.flag;
    }
    
}
/**
 * Your WordDictionary object will be instantiated and called as such:
 * WordDictionary obj = new WordDictionary();
 * obj.addWord(word);
 * boolean param_2 = obj.search(word);
 */
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值