二叉搜索树节点添加与中序遍历原理咨询
BST的addNode与inOrderTraversal递归逻辑及this用法解析
先贴一段典型的Java BST实现代码(匹配你提到的方法结构),所有讲解基于这段代码展开:
class BST { // 内部节点类 class Node { int val; Node left; Node right; Node(int val) { this.val = val; } } private Node root; // 对外暴露的添加入口 public void add(int val) { root = addNode(root, val); } // 核心递归添加方法 private Node addNode(Node current, int val) { // 递归终止:找到插入位置,返回新节点 if (current == null) { return new Node(val); } // 递归分支:处理左/右子树 if (val < current.val) { current.left = addNode(current.left, val); } else if (val > current.val) { current.right = addNode(current.right, val); } // 重复值不处理(BST默认规则) return current; } // 对外暴露的中序遍历入口 public void inOrder() { inOrderTraversal(root); } // 核心递归中序遍历方法 private void inOrderTraversal(Node current) { // 递归终止:空节点直接返回 if (current == null) { return; } // 中序顺序:左子树 → 当前节点 → 右子树 inOrderTraversal(current.left); System.out.print(current.val + " "); inOrderTraversal(current.right); } }
一、addNode方法的递归逻辑
递归的本质是把大问题拆成重复的小问题,每次只处理当前节点,子树交给递归调用:
- 终止条件:当
current为null时,说明找到了空的插入位置,直接返回一个新的Node对象。 - 分支处理:
- 若插入值
val小于当前节点的val,则递归处理左子树,把返回的节点(新节点或原左子树节点)赋值给current.left,更新左子树的引用。 - 若
val大于当前节点的val,同理递归处理右子树,更新current.right。 - 若值相等,直接返回原节点(BST通常不存储重复值)。
- 若插入值
- 返回当前节点:每次递归调用都会更新子节点的引用,返回当前节点是为了让上层调用能正确更新父节点的左/右指针,保证树结构的完整性。
实例演示(插入序列:5→3→7)
- 第一次调用
addNode(null,5):返回新节点Node(5),root被赋值为该节点。 - 插入3:调用
addNode(root(5),3),3<5,触发addNode(null,3)返回Node(3),赋值给5.left,最终返回5,root不变。 - 插入7:调用
addNode(root(5),7),7>5,触发addNode(null,7)返回Node(7),赋值给5.right,最终返回5,root不变。
二、inOrderTraversal方法的递归逻辑
中序遍历的固定顺序是左子树 → 当前节点 → 右子树,递归严格遵循这个顺序执行:
- 终止条件:当
current为null时,直接返回(空树或叶子节点的子树无需处理)。 - 执行步骤:
- 先递归遍历左子树:一直深入到左子树的最底层叶子节点的左子节点(
null),触发终止返回。 - 然后访问当前节点:这里的“访问”是打印节点值(你也可以改成其他操作)。
- 最后递归遍历右子树:同样深入到右子树的最底层叶子节点的右子节点(
null),触发终止返回。
- 先递归遍历左子树:一直深入到左子树的最底层叶子节点的左子节点(
实例演示(基于上面的树:根5,左3,右7)
- 调用
inOrderTraversal(5),先执行inOrderTraversal(3)。 inOrderTraversal(3)先调用inOrderTraversal(null)(返回),然后打印3,再调用inOrderTraversal(null)(返回),回到上层。- 回到
inOrderTraversal(5),打印5,然后执行inOrderTraversal(7)。 inOrderTraversal(7)先调用inOrderTraversal(null)(返回),打印7,再调用inOrderTraversal(null)(返回),结束。- 最终输出:
3 5 7
三、this关键字结合点符号的用法
在这段代码里,this的作用非常明确,分两种场景:
- Node构造器中:
this.val = valthis指代当前正在创建的Node实例,用来区分成员变量val和构造器的参数val(变量重名时必须用this明确指向成员变量)。
- BST类的方法中(比如
this.root)this指代当前BST对象实例,用来明确访问的是当前实例的成员变量root。如果没有重名变量,this可以省略,但写上能让代码更清晰,避免歧义。
四、递归图绘制技巧(帮你理清逻辑)
如果画递归图,建议结合调用栈+节点树:
- 每一次递归调用就往调用栈里加一层,标注当前处理的节点和参数。
- 遇到终止条件(
current为null)时,从栈顶开始返回,标注返回值(新节点或null)。 - 对于遍历方法,标注每一步的“访问”(打印)动作。
比如addNode插入3的调用栈:
1. addNode(5,3) → 进入,判断3<5,调用addNode(null,3) 2. addNode(null,3) → 终止,返回Node(3) 回到1:将5.left赋值为Node(3),返回5
中序遍历的调用栈:
1. inOrderTraversal(5) → 调用inOrderTraversal(3) 2. inOrderTraversal(3) → 调用inOrderTraversal(null) 3. inOrderTraversal(null) → 终止,返回 回到2:打印3,调用inOrderTraversal(null) 4. inOrderTraversal(null) → 终止,返回 回到1:打印5,调用inOrderTraversal(7) 5. inOrderTraversal(7) → 调用inOrderTraversal(null) 6. inOrderTraversal(null) → 终止,返回 回到5:打印7,调用inOrderTraversal(null) 7. inOrderTraversal(null) → 终止,返回 回到1:结束
内容的提问来源于stack exchange,提问作者Mostack
相关产品推荐
相关产品推荐

