DFS over string trie(前缀)

时间:2013-05-04 22:48:44

标签: java algorithm tree prefix-tree

我写了以下前缀trie:

class TrieNode {
    char letter;
    HashMap<Character,TrieNode> children;
    boolean fullWord;

    TrieNode(char letter) {
        this.letter = letter;
        children = new  HashMap<Character, TrieNode>();
        this.fullWord = false;
    }
}

class Tree{
    static TrieNode createTree() {
         return (new TrieNode('\0'));
    }

    static void insertWord(TrieNode root, String word) {
        int l = word.length();
        char[] letters = word.toCharArray();
        TrieNode curNode = root;
        for (int i = 0; i < l; i++) {
            if (!curNode.children.containsKey(letters[i]))
                curNode.children.put(letters[i], new TrieNode(letters[i]));
            curNode = curNode.children.get(letters[i]);
        }
        curNode.fullWord = true;
    }
}

我想添加DFS方法,以便找到第一个包含多个子节点的节点(因此它会显示最长的公共前缀)。

我写了这段代码:

static void DFS(TrieNode node) {
    for (TrieNode tn: node.children.values()){
       DFS(tn);
        if (node.children.size()>2)
        System.out.println("found the first vertex");
    }
}

但它不起作用。我做错了什么?

2 个答案:

答案 0 :(得分:1)

您的前缀树代码似乎很好。至少它对我有意义,但我没有检查所有角落案件。问题是你的DFS方法。假设我们有以下由前缀树组成的字符串:

- "abcd"
- "abcg"
- "abckk"
- "abf"

所以前缀树应如下所示:

              root
               |
               a
               |
               b
              / \
             c   f
            /|\
           d g k
                \
                 k

我认为你所期望的是我们可以在节点 b 输出“找到第一个顶点”(因为很明显“ab”是最常见的上面四个字符串的前缀),但是,你的DFS不能那样工作。它会一路走来

root->a->b->c->d then back to c, finding that c.children.size() >1 , then output.

请注意,“ 2或更多”是&gt; = 2或&gt; 1,在您的程序中>> 2。我认为这应该是一个错字。 要更正您的DFS,只需修改它以检查节点的子项大小是否会起作用:

static boolean DFS(TrieNode node) {
    if (node.children.size()>1){
        System.out.println("found the first vertex on node:"+node.letter);
        return true;
    }
    for (TrieNode tn: node.children.values()){
        if(DFS(tn))
            return true;
    }
    return false;
}

注意让程序在找到我们的节点时停止,我修改了你DFS的返回类型。 此外,关于找到最长公共前缀,DFS可能不是这里的最佳选择,以下代码在运行时复杂性方面优于DFS:

static void lcp(TrieNode node){
    TrieNode first = node;
    while(first.children.size()==1)
        first = first.children.values().iterator().next();
    System.out.println("found the first vertex on node:"+first.letter);
}

答案 1 :(得分:1)

首先,我们需要澄清最长公共前缀这里是指特里树中任意两个或更多字符串的最长公共前缀。

因此,您的DFS方法将无法正常工作,因为它只是遍历整个树,并在访问其children.size()&gt;的任何节点时输出“找到第一个顶点”。 2(它应该是&gt; = 2

我们想要的只是找到最长公共前缀。所以我们需要一些关于哪个是最长的信息的额外信息。在上面的例子中很容易看到:

          root      --depth: 0
           |
           a        --depth: 1
           |
           b        --depth: 2
          / \
         c   f      --depth: 3
        /|\
       d g k        --depth: 4
            \
             k      --depth: 5 

最长的公共前缀节点具有children.size()&gt; 1 AND 具有最大深度。在这种情况下,它的节点是 c

所以这是一个可能正确的DFS:

static int max=-1;
static TrieNode maxNode=null;

static void dfs(TrieNode node, int depth){
    if(node.children.size()>1 && depth>max){
        max=depth;
        maxNode=node;
    }
    for (TrieNode tn: node.children.values())
        dfs(tn,depth+1);
}

public static void test(){
    TrieNode root = Tree.createTree();
    Tree.insertWord(root, "abcd");
    Tree.insertWord(root, "abcg");
    Tree.insertWord(root, "abckk");
    Tree.insertWord(root, "abf");
    dfs(root,0);
    System.out.println("Max node:"+maxNode.letter);
}

运行dfs后, maxNode 将保留最长公共前缀停止的节点。在这种情况下它是节点c。