二叉树节点赋值程序报错求助及实现方式合理性咨询
嘿,我来帮你搞定这个问题!首先咱们先解决那个烦人的NumberFormatException,然后再聊聊节点赋值的实现方式~
一、先搞定NumberFormatException的问题
你遇到的这个异常,说白了就是**Scanner.nextInt()读取完节点总数后,输入流里还残留着换行符**,紧接着调用in.nextLine()会直接读取这个空的换行,导致input数组里出现空字符串,调用Integer.parseInt("")自然就抛异常了。
修复方法超简单,在读取完N之后,加一行代码吃掉这个多余的换行:
int N = in.nextInt(); in.nextLine(); // 新增这行,清除输入流中的换行符
另外注意你的示例输入格式——你给的是一整行,但你的代码逻辑是每次in.nextLine()读取一行,所以要么把示例输入拆成多行(第一行是10,第二行是0 10,第三行是1 19 LEFT 0,以此类推),要么修改代码逻辑,一次性读取所有输入再分割处理,这样更稳健:
// 替换原来的输入读取逻辑,适配整行输入 String allInput = in.nextLine(); String[] parts = allInput.split(" "); int ptr = 0; int N = Integer.parseInt(parts[ptr++]); BNode<String> nodes[] = new BNode[N]; BTree<String> tree = new BTree<>(); // 处理根节点 int rootIdx = Integer.parseInt(parts[ptr]); nodes[rootIdx] = new BNode<>(parts[ptr+1]); tree.root = nodes[rootIdx]; ptr += 2; // 处理子节点 for (int i = 1; i < N; i++) { int nodeIdx = Integer.parseInt(parts[ptr]); String val = parts[ptr+1]; String dir = parts[ptr+2]; int parentIdx = Integer.parseInt(parts[ptr+3]); nodes[nodeIdx] = new BNode<>(val); if (dir.equals("LEFT")) tree.addNode(nodes[parentIdx], BNode.LEFT, nodes[nodeIdx]); else tree.addNode(nodes[parentIdx], BNode.RIGHT, nodes[nodeIdx]); ptr += 4; }
这样不管输入是一行还是多行,都能正确处理,再也不会被换行坑到啦。
二、当前节点赋值方式的合理性与优化方向
1. 当前方式的适用场景
你现在用数组存储所有节点、通过序号关联父子节点的方式,在已知节点总数、且节点序号连续的场景下是完全可行的——逻辑清晰,查找父节点的速度是O(1),适合小范围的二叉树构建。但它的局限性也很明显:
- 必须提前知道节点总数,灵活性差;
- 节点序号必须连续,否则会浪费数组空间;
- 耦合性高,依赖序号来关联节点,代码的可读性和扩展性一般。
2. 实际开发中更受欢迎的实现方式
(1)用Map替代数组存储节点
换成Map<Integer, BNode<String>>来存储节点,不需要提前知道总数,也不要求序号连续,灵活度拉满:
Map<Integer, BNode<String>> nodeMap = new HashMap<>(); // 处理根节点 int rootIdx = Integer.parseInt(parts[ptr]); String rootVal = parts[ptr+1]; BNode<String> root = new BNode<>(rootVal); nodeMap.put(rootIdx, root); tree.root = root; // 处理子节点时,直接从Map取父节点 BNode<String> parent = nodeMap.get(parentIdx); if (dir.equals("LEFT")) { parent.setLeft(node); } else { parent.setRight(node); }
这种方式在节点序号不连续、总数不确定的场景下特别实用。
(2)让节点类自身管理子节点关联
简化树类的职责,让BNode类自己提供添加子节点的方法,代码更符合面向对象的设计:
class BNode<T> { private T val; private BNode<T> left; private BNode<T> right; public BNode(T val) { this.val = val; } // 添加左子节点 public void setLeft(BNode<T> left) { this.left = left; } // 添加右子节点 public void setRight(BNode<T> right) { this.right = right; } // 按需添加getter方法 }
这样构建树的时候,直接调用节点的方法关联,不需要树类的addNode方法,职责更清晰,代码也更简洁。
(3)使用构建器(Builder)模式封装构建逻辑
如果二叉树的结构比较复杂,或者需要支持多种构建方式,可以用Builder模式把构建逻辑封装起来,可读性和扩展性都很强:
class BTreeBuilder<T> { private Map<Integer, BNode<T>> nodeMap = new HashMap<>(); private BNode<T> root; public BTreeBuilder<T> addNode(int idx, T val, String dir, Integer parentIdx) { BNode<T> node = new BNode<>(val); nodeMap.put(idx, node); if (parentIdx == null) { // 根节点,没有父节点 root = node; } else { BNode<T> parent = nodeMap.get(parentIdx); if (dir != null && dir.equals("LEFT")) { parent.setLeft(node); } else { parent.setRight(node); } } return this; } public BTree<T> build() { BTree<T> tree = new BTree<>(); tree.root = root; return tree; } }
使用的时候就像搭积木一样:
BTree<String> tree = new BTreeBuilder<String>() .addNode(0, "10", null, null) .addNode(1, "19", "LEFT", 0) .addNode(2, "8", "LEFT", 1) // ... 依次添加其他节点 .build();
(4)递归/迭代方式从遍历序列构建
如果输入是前序/中序/后序遍历的序列,或者是类似JSON的结构化数据,可以用递归的方式直接构建树,这种方式在算法题和实际开发中都很常用。比如给定前序和中序遍历结果,递归生成每个节点,自动关联父子关系。
内容的提问来源于stack exchange,提问作者user8108550

