【问题标题】:Working with Java Trie, fails to identify word endings. Fails somewhere in recursion使用 Java Trie,无法识别词尾。在递归的某个地方失败
【发布时间】:2013-05-11 02:26:47
【问题描述】:

我正在尝试创建我自己的 Java Trie 版本,以便拥有一个,并获得制作它所需的知识,但这个项目让我感到困惑。我这里有一个非常基本的损坏的 Trie。

我正在向 Trie 添加 3 个单词(使用单词的第一个字符作为键,值是附加的 TrieNodes)。如果您注意到,我在 TrieNode 类中有打印语句,用于在程序运行时检查 isWord 的值。我希望单词中的最后一个字符将 isWord 变量设置为 True。从而识别单词的结尾(我稍后将使用它来获取整个单词)。

当我第一次将三个单词放入时,输出会打印每个单词的每个字母,因为它们进入 Trie 并正确识别哪些字符是单词结尾。

但是,如果在输入单词后立即遍历 Trie 并重新打印 Trie 中的所有字符及其 isWord 状态,“Hello”中的 'e' 现在突然被识别为单词结尾?

我已经为此倾注了好几个小时,但我只是不明白为什么会发生这种情况。以下是工作代码:

package testcode;


import java.util.*;

public class TestCode {

    public static Trie t;
    public static void main (String[] args){
        t = new Trie();
        t.addWord("hello");
        t.addWord("hi");
        t.addWord("soup");
        //at this point the output correctly identifies word endings.
        t.findWords();
        /* but when iterating through the hash map it becomes evident that
        * when entering the word 'hi' the 'e' in 'hello' had its isWord variable
        * changed to true. I followed the logic and I do not see how or why this
        * is happening.
        */
    }
}

//This Trie class handles the root trie, and Trie commands.
class Trie{
    private TrieNode root;

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

    public void addWord(String word){
        root.addWord(word.toLowerCase());
    }

    public void findWords(){
        root.findWords();
    }
}

//Trie Node handles the nodes and words within the trie
class TrieNode{

    private TrieNode parent;
    private boolean isWord;
    private boolean hasChildren;
    private char character;
    private Map<Character, TrieNode> children = new HashMap<>();

    public TrieNode(){

        hasChildren = false;
        isWord = false;
    }

    public TrieNode(String word){

        this();
        addWord(word);

    }
    public void addWord(String word){

       char firstChar = word.charAt(0);


       if (children.get(firstChar) == null){

           if(word.length() > 1){

               hasChildren = true;
               children.put(firstChar, new TrieNode(word.substring(1)));
               children.get(firstChar).parent = this;
               System.out.print(firstChar + "--");
               System.out.println(isWord);
           }

           else{
               children.put(firstChar, new TrieNode());
               if(character == 'e'){
                   System.out.println("shits about to go down");
               }
               isWord = true;
               System.out.print(firstChar + "--");
               System.out.println(isWord);
           }
           children.get(firstChar).character = firstChar;
       }

       else {
           children.get(firstChar).addWord(word.substring(1));
       }
   }

    public void findWords(){
        for(Character key : children.keySet()){
            children.get(key).findWords();
            System.out.println(children.get(key).character + " -- " + isWord);     
      }
    }
}

此代码生成以下输出:

o--true
l--false
l--false
e--false
h--false
i--true
p--true
u--false
o--false
s--false
p -- true
u -- false
o -- false
s -- false
o -- true
l -- false
l -- false
e -- true    //notice the e here is now suddenly a word ending with isWord = true
i -- true
h -- false

【问题讨论】:

    标签: java recursion hashmap trie


    【解决方案1】:

    存在一系列可能的问题。父/子混淆,在父节点处理叶案例,包括构建和打印输出等。

    我注意到在您的旧 'findWords' 代码中,您打印的是 child characterparent 'isWord' flag。构建特里树在“子节点存在”和“创建子节点路径”之间存在不合需要的分歧——因此“isWord”只能在新路径上标记,而不能在现有路径上标记。构建 trie 似乎也将 'isWord' 设置在父节点而不是叶节点上。

    通常,嵌套 IF 案例的意大利面条式代码很可能是不可靠的。代码应该尽可能通用 - 将其保留在方法的主要流程中,除非它真的确实属于 IF。

    这是干净且正确的代码:

    class TrieNode{
        private TrieNode parent;
        private boolean isWord;
        private boolean hasChildren;
        private char character;
        private Map<Character, TrieNode> children = new HashMap<>();
    
        public TrieNode(){
            this.hasChildren = false;
            this.isWord = false;
        }
        public TrieNode (char ch) {
            this.character = ch;
            this.hasChildren = false;
            this.isWord = false;
        }
    
        public void addWord (String word){
            if (word.length() == 0) {
                this.isWord = true;
                System.out.println( character + " -- " + isWord);
                return;
            }
    
            // represent the Child Node;
            //       --
            char firstChar = word.charAt(0);
            TrieNode child = children.get( firstChar);
            if (child == null){
                child = new TrieNode( firstChar);
                children.put( firstChar, child);
                child.parent = this;
                hasChildren = true;
            }
    
            // add Remaining Word;
            //      -- call for 1-length words, as 0-length at Child sets 'IsWord'!
            child.addWord( word.substring(1));
    
            // print building here.
            System.out.println( character + " -- " + isWord);
        }
    
    
    
        public void findWords(){
            for(Character key : children.keySet()){
                children.get(key).findWords();
            }
            System.out.println( character + " -- " + isWord);     
        }
    }
    

    【讨论】:

    • 我非常感谢您的回复。不过,我喜欢你替换 If 语句的方法 One question,这来自我对亲子关系的明显误解。变量 in 和 this.variable 有什么区别。我了解“this”关键字的作用,但在这个程序中,它们在任何给定点都不应该相同吗? IE:在 addWord() 方法中有一个 this.isWord = true ,然后下一行仅使用 'isWord' 而不是 this.isWord。这两个变量是不同的还是它们都指的是同一个(当前节点)isWord?
    • 我总是用this. 写字段分配,以使它们非常清晰明确。 (在我的设置器中,我不会为参数添加前缀或将其命名为与字段不同的名称。我只依赖this.)。效果很好,阅读清晰。
    猜你喜欢
    • 1970-01-01
    • 2021-11-23
    • 2013-01-01
    • 2012-10-07
    • 1970-01-01
    • 2012-05-04
    • 1970-01-01
    • 2011-04-10
    • 1970-01-01
    相关资源
    最近更新 更多