如何从路径编码构造二叉树?

时间:2015-01-29 11:43:26

标签: java encoding path binary-search-tree

假设我输入以下表格:

(11,LL)(7,LLL)(8,R)(5,)(4,L)(13,RL)(2,LLR)(1,RRR)(4,RR)()< / p>

其中第二个字段表示来自根节点的路径,空字段表示根节点,()表示数据的结束。

输出将是 levelorder:5 4 8 11 13 4 7 2 1

如何重建二叉树?请注意,可能存在节点丢失的可能性。例如,存在LLL和L,但没有节点连接它们,对于这种情况,应该创建值为-1的节点来连接它们。

到目前为止我想到的是创建一个存储数据和路径的NodePath类,例如NodePath(11,LL)都是字符串值。接下来,我遍历每个字符串标记。在迭代期间,我比较路径长度并将它们存储在LinkedList中。 例如。 11,LL - &gt; 7,LLL,当8到达时,它变为8,R - > 11,LL - &gt; 7,LLL

现在,这就是我被困住的地方,因为我不知道如何区分LLL和LLR以及如何相应地构造二叉树。我担心我会把LLL和LLR放在相反的位置。

3 个答案:

答案 0 :(得分:1)

您可以使用键值对构建Map - 键是您的路径(“LLL”)并为数字(“7”)赋值。

在第二步中迭代Map的EntrySet并自上而下构建每个路径,创建默认值为“-1”的新节点,其中没有任何节点,并且尽可能使用现有节点。

答案 1 :(得分:1)

这是一个有趣的问题。我建议让树节点能够跟踪构建节点的路径。我将忽略解码字符串的所有代码并跳转到构建的核心:

class Node {
    public enum Direction {
        L, R;
    }

    private int value = -1;
    private EnumMap<Direction, Node> children = new EnumMap<>(Direction.class);

    public Node nodeWithPath(List<Direction> path) {
        if (path.isEmpty()) {
            return this;
        } else {
            Direction direction = path.remove(0);
            if (!children.containsKey(direction))
                children.put(direction, new Node());
            return children.get(direction).nodeWithPath(path);
        }
    }
}

当您解码以下指令时,这很简单:

root.nodeWithPath(path).setValue(value);

作为(可能的)帮助的另一部分,通过使用&#39; L&#39;和&#39; R&#39;作为枚举常量,您可以从字符串中简单地在Java 8中创建路径:

public void addValue(Node root, String pathString, int value) {
    List<Direction> path = Arrays.stream(pathString.toCharArray())
        .map(Character::toString)
        .map(Node.Direction::valueOf)
        .collect(Collectors.toList());
    root.nodeWithPath(path).setValue(value);
}

然而,如果您在学习Java的过程中处于早期状态,那么我刚刚编写的代码可能会比帮助更加混乱,因此可以随意忽略它并使用传统迭代或通过标记化解析: - )

答案 2 :(得分:0)

以下是基于grappa的解决方案。

首先,节点的基本类:

public final class BinaryTreeNode
{
    private int value = -1;
    private BinaryTreeNode left = null;
    private BinaryTreeNode right = null;

    public int getValue()
    {
        return value;
    }

    public BinaryTreeNode getLeft()
    {
        return left;
    }

    public BinaryTreeNode getRight()
    {
        return right;
    }

    public void setValue(final int value)
    {
        this.value = value;
    }

    public void setLeft(final BinaryTreeNode left)
    {
        this.left = left;
    }

    public void setRight(final BinaryTreeNode right)
    {
        this.right = right;
    }
}

现在是构建器类:

public final class BinaryTreeBuilder
{
    private final BinaryTreeNode root = new BinaryTreeNode();

    private BinaryTreeNode current = root;

    private int nodeValue;

    public BinaryTreeNode getRoot()
    {
        return root;
    }

    public boolean setValue(final String match)
    {
        nodeValue = Integer.parseInt(match);
        return true;
    }

    public boolean moveLeft()
    {
        BinaryTreeNode node = current.getLeft();
        if (left == null) {
            node = new BinaryTreeNode();
            current.setLeft(node);
        }
        current = node;
        return true;
    }

    public boolean moveRight()
    {
        BinaryTreeNode node = current.getRight();
        if (right == null) {
            node = new BinaryTreeNode();
            current.setRight(node);
        }
        current = node;
        return true;
    }

    public boolean commitNode()
    {
        current.setValue(nodeValue);
        current = root;
        return true;
    }
}

最后是解析器:

public class BinaryTreeParser
    extends BaseParser<BinaryTreeNode>
{
    protected final BinaryTreeBuilder = new BinaryTreeBuilder();

    Rule endOfData()
    {
        return sequence("()", EOI);
    }

    Rule moves()
    {
        return firstOf(
            sequence('L', builder.moveLeft()),
            sequence('R', builder.moveRight())
        );
    }

    Rule oneNode()
    {
        return sequence(
            '(',
            sequence(oneOrMore(digit()), builder.setValue(match())),
            ',',
            zeroOrMore(moves()),
            ')', builder.commitNode();
        );
    }

    Rule allNodes()
    {
        return join(sequence(testNot(endOfData()), oneNode())
            .using(' ')
            .min(0);
    }

    public Rule fullTree()
    {
        return sequence(allNodes(), endOfData(), push(builder.getRoot());
    }
}

现在你只需要运行解析器并收集结果:

final BinaryTreeParser parser = Parboiled.createParser(BinaryTreeParser.class);

final ParseRunner<BinaryTreeNode> runner = new BasicParseRunner<>(parser.fullTree());

final ParsingResult<BinaryTreeNode> result = runner.run(yourInput);

if (result.isSuccess())
    tree = result.getTopStackValue();

由于格拉巴依赖于番石榴而Guave提供BinaryTreeTraverser,您可以使用它来显示结果。

当然,这种方法相当复杂&#34 ;;这是一个更简单的问题:

  • 创建一个Comparator<String>,其比较如下:如果字符串更长,则字符串大于另一个字符串;如果它们的长度相等,则适用规范排序;
  • 创建一个TreeMap<String, Integer>,路径为键,节点值为值,上面的比较器为关键比较器;
  • 将您的节点解析到地图中;
  • 按顺序显示条目。

但是请注意,您需要在显示之前检查您是否有根条目,即空字符串。