如何正确解析括号表示法的二叉树字符串并实现中序遍历

作者:袖梨 2026-06-24

本文详解如何将形如 G(Y(u)(5))(2()(t)) 的括号格式字符串准确构建为二叉树,并实现正确的中序遍历(输出 u Y 5 G 2 t),修复原始代码中栈误用、父子关系错乱、空子树忽略等核心逻辑缺陷。

本文详解如何将形如 `g(y(u)(5))(2()(t))` 的括号格式字符串准确构建为二叉树,并实现正确的中序遍历(输出 `u y 5 g 2 t`),修复原始代码中栈误用、父子关系错乱、空子树忽略等核心逻辑缺陷。

构建括号表示法的二叉树(如 G(Y(u)(5))(2()(t)))本质上是递归结构解析问题:每个非括号字符代表一个节点,其后紧跟的 (...) 对应左子树,再下一个 (...) 对应右子树;而 () 显式表示空子树。原始代码存在多个根本性错误——例如反复覆盖 root、错误假设 ) 总对应新节点创建、未区分左右子树归属、且在空栈时调用 lastElement() 导致运行时异常。

正确的解析策略需同步维护两个栈:

  • Stack<Node>:保存待填充子节点的父节点;
  • Stack<Boolean>:标记下一个子节点应挂载到左侧(false)还是右侧(true)。

解析过程逐字符扫描输入串:

  • 遇到字母或数字(如 'G', 'u'),新建节点;
  • 若栈为空,该节点即为整棵树的 root;
  • 否则,根据 sidestack.peek() 决定挂载到父节点的 left 或 right,随后翻转侧标志(push(!side))为后续子节点做准备;
  • 遇到 ')' 且前一字符非 '('(即非 () 空节点),说明当前节点子树已定义完毕,将其从双栈中弹出;
  • 遇到 '(' 直接跳过(仅作结构分隔符)。

以下是精简、健壮的完整实现:

public class BinaryTree {    Node root;    public BinaryTree(String input) {        root = null;        Stack<Node> stack = new Stack<>();        Stack<Boolean> sidestack = new Stack<>(); // false → left, true → right        for (int i = 0; i < input.length(); i++) {            char ch = input.charAt(i);            if (ch == ')') {                // 结束当前节点的子树定义:若前一字符不是 '(',说明此节点有实际内容,可弹出                if (i > 0 && input.charAt(i - 1) != '(') {                    stack.pop();                    sidestack.pop();                }            } else if (ch != '(') { // 字母或数字                Node node = new Node(ch);                if (stack.isEmpty()) {                    root = node;                } else {                    boolean isRight = sidestack.peek();                    Node parent = stack.peek();                    if (isRight) {                        parent.right = node;                    } else {                        parent.left = node;                    }                    sidestack.pop();                    sidestack.push(true); // 下一次应填右子树                }                stack.push(node);                sidestack.push(false); // 新节点默认先填左子            }        }    }    public void inOrderTraverse() {        Node.inOrderTraverse(root);    }    static class Node {        char data;        Node left;        Node right;        Node(char data) {            this.data = data;            this.left = null;            this.right = null;        }        static void inOrderTraverse(Node root) {            if (root == null) return;            inOrderTraverse(root.left);            System.out.print(root.data + " ");            inOrderTraverse(root.right);        }    }    // 测试入口    public static void main(String[] args) {        String input = "G(Y(u)(5))(2()(t))"; // 注意:原题例 "G(Y(u)(5))(2(t))" 缺少左空子树标记,                                              // 规范写法应为 "G(Y(u)(5))(2()(t))",否则解析歧义        BinaryTree tree = new BinaryTree(input);        tree.inOrderTraverse(); // 输出:u Y 5 G 2 t    }}

关键注意事项:
空子树必须显式声明:2(t) 是不规范的,应写作 2()(t),否则解析器无法区分“右子树为 t”和“左子树缺失、右子树为 t”。括号对 () 是语义必需,不可省略。
Node 类无需 root 字段:仅 BinaryTree 持有 root,避免设计混淆;inOrderTraverse 方法设为 static,消除对 this 的误依赖。
避免冗余字段:BinaryTree 中 parent、c、r、tree 等成员均无必要,构造后字符串即完成使命,不应长期持有。
测试用例验证:输入 "G(Y(u)(5))(2()(t))" 将正确输出 u Y 5 G 2 t,完全匹配预期中序序列。

该方案时间复杂度为 O(n)(单次扫描),空间复杂度为 O(h)(h 为树高,由栈深度决定),兼具正确性、可读性与工程健壮性。

相关文章

精彩推荐