You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

二叉树节点赋值程序报错求助及实现方式合理性咨询

解决二叉树节点赋值的异常问题与实现优化建议

嘿,我来帮你搞定这个问题!首先咱们先解决那个烦人的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 04:52:41