前置:
classTreeNode{intval;TreeNodeleft;TreeNoderight;publicTreeNode(intval){this.val=val;}}publicclassMain{// 前序遍历(静态方法,直接传入根节点)publicstaticvoidpreOrder(TreeNodenode){if(node==null)return;System.out.print(node.val+" ");preOrder(node.left);preOrder(node.right);}publicstaticvoidinOrder(TreeNodenode){if(node==null)return;inOrder(node.left);System.out.print(node.val+" ");inOrder(node.right);}publicstaticvoidpostOrder(TreeNodenode){if(node==null)return;postOrder(node.left);postOrder(node.right);System.out.print(node.val+" ");}publicstaticTreeNodesearch(TreeNodenode,inttarget){if(node==null)returnnull;if(node.val==target)returnnode;TreeNodeleft=search(node.left,target);if(left!=null)returnleft;returnsearch(node.right,target);}}Java数据结构期末机考应用题:手撕二叉树的遍历与查找(逐行注释版)
适用场景:Java 数据结构期末机考 · 手写代码题(教师人工批改)
特点:每一行代码均有中文注释,便于理解与记忆
在期末机考中,虽然使用电脑答题,但评分由老师人工完成。因此,清晰的逻辑 + 逐行注释能极大提升阅卷印象分。本文提供一份完整、简洁、每行带注释的二叉树遍历与查找实现,助你精准踩中得分点!
完整代码(逐行中文注释)
// 定义二叉树节点类,用于表示树中的每一个节点classTreeNode{intval;// 节点存储的整数值TreeNodeleft;// 指向左子节点的引用(若无左孩子则为 null)TreeNoderight;// 指向右子节点的引用(若无右孩子则为 null)// 构造方法:通过传入的值初始化当前节点publicTreeNode(intval){this.val=val;// 将参数 val 赋值给当前节点的 val 字段}}// 二叉树主类,包含遍历和查找功能publicclassBinaryTree{privateTreeNoderoot;// 根节点引用,代表整棵二叉树的起点// 设置根节点的方法,用于外部构建测试用的树结构publicvoidsetRoot(TreeNoderoot){this.root=root;// 将传入的节点设为当前树的根节点}// 获取根节点的方法,便于外部访问树的根publicTreeNodegetRoot(){returnroot;// 返回当前树的根节点}// 前序遍历方法:访问顺序为“根 → 左子树 → 右子树”publicvoidpreOrder(TreeNodenode){if(node==null)return;// 如果当前节点为空,直接返回(递归终止条件)System.out.print(node.val+" ");// 先访问并打印当前节点的值preOrder(node.left);// 递归遍历当前节点的左子树preOrder(node.right);// 递归遍历当前节点的右子树}// 中序遍历方法:访问顺序为“左子树 → 根 → 右子树”publicvoidinOrder(TreeNodenode){if(node==null)return;// 如果当前节点为空,直接返回(递归终止条件)inOrder(node.left);// 先递归遍历当前节点的左子树System.out.print(node.val+" ");// 再访问并打印当前节点的值inOrder(node.right);// 最后递归遍历当前节点的右子树}// 后序遍历方法:访问顺序为“左子树 → 右子树 → 根”publicvoidpostOrder(TreeNodenode){if(node==null)return;// 如果当前节点为空,直接返回(递归终止条件)postOrder(node.left);// 先递归遍历当前节点的左子树postOrder(node.right);// 再递归遍历当前节点的右子树System.out.print(node.val+" ");// 最后访问并打印当前节点的值}// 查找方法:在以 node 为根的子树中查找值等于 target 的节点publicTreeNodesearch(TreeNodenode,inttarget){if(node==null)returnnull;// 若当前节点为空,说明未找到,返回 nullif(node.val==target)returnnode;// 若当前节点的值等于目标值,返回该节点(找到!)TreeNodeleftResult=search(node.left,target);// 递归在左子树中查找目标值if(leftResult!=null)returnleftResult;// 如果左子树找到了,直接返回结果returnsearch(node.right,target);// 否则,在右子树中继续查找并返回结果(可能为 null)}}为什么这样写?
- ✅每行都有注释:老师批改时能快速确认你理解了代码含义;
- ✅仅使用递归:机考时间紧,递归写法简洁、不易出错;
- ✅结构清晰:先定义节点,再实现功能,符合认知逻辑;
- ✅边界处理到位:所有方法都包含
node == null判断,避免空指针异常; - ✅命名规范:变量名如
target、leftResult语义明确,提升可读性。
考试书写建议
- 在机考答题区直接粘贴或手敲上述代码;
- 若题目不要求
getRoot(),可省略; - 不需要写
main方法(除非题目明确要求); - 保持缩进一致,即使系统不自动格式化,也要手动对齐(老师看得舒服=印象分高)。
总结
掌握二叉树的遍历与查找,关键在于理解递归的执行流程和访问顺序的差异。这份逐行注释的代码,不仅帮你应对考试,更能加深对数据结构本质的理解。
📌记住:考试不是比谁写得快,而是比谁写得准、清、稳!
祝你期末机考顺利,轻松拿下这道必考题!
// 构造方法:通过传入的值初始化当前节点 public TreeNode(int val) { this.val = val; // 将参数 val 赋值给当前节点的 val 字段 }}
这个不写会怎么样?
这是一个非常好的问题!我们来详细分析:
❓如果不写public TreeNode(int val)构造方法,会发生什么?
✅ 情况一:你完全不提供任何构造方法
classTreeNode{intval;TreeNodeleft;TreeNoderight;// 没有写任何构造方法}后果:
Java 会自动提供一个无参的默认构造方法(public TreeNode()),但不会提供带参数的构造方法。
所以,当你在代码中尝试这样创建节点时:
TreeNoderoot=newTreeNode(1);// ❌ 编译错误!你会得到编译错误:
error: constructor TreeNode in class TreeNode cannot be applied to given types; required: no arguments found: int💥结论:无法用
new TreeNode(1)初始化节点值,程序根本编译不过!
✅ 情况二:你写了其他构造方法,但没写TreeNode(int val)
比如只写了无参构造:
classTreeNode{intval;TreeNodeleft;TreeNoderight;publicTreeNode(){}// 只提供无参构造}那么同样:
newTreeNode(1);// ❌ 依然报错!因为没有匹配的构造方法你只能这样写:
TreeNodenode=newTreeNode();node.val=1;// 手动赋值虽然能运行,但在构建树时非常繁琐、易错、不优雅,尤其在机考中浪费时间。
✅ 为什么考试/面试中一定要写TreeNode(int val)构造方法?
- 简洁性:
new TreeNode(5)一行搞定; - 安全性:确保节点创建时就有有效值;
- 符合惯例:所有教材、题解、LeetCode 都这么写;
- 避免遗漏:如果忘记手动赋值
val,节点值默认为0(int 默认值),可能导致逻辑错误(比如你要建值为 0 的节点 vs 忘记赋值)。
⚠️ 特别注意:
int val是基本类型,默认值是0。如果你不赋值,所有节点的val都是 0,遍历输出全是 0,查找也会出错!
✅ 正确做法(考试推荐)
始终提供一个带val参数的构造方法:
publicTreeNode(intval){this.val=val;// 立即初始化节点值// left 和 right 默认为 null,无需显式赋值(引用类型默认值就是 null)}这样你才能在机考中快速构建测试树:
TreeNoderoot=newTreeNode(1);root.left=newTreeNode(2);root.right=newTreeNode(3);// ...📌 总结
是否写TreeNode(int val)构造方法 | 能否用new TreeNode(1)? | 是否适合考试? |
|---|---|---|
| ❌ 不写 | ❌ 不能,编译报错 | ❌ 不行 |
| ✅ 写了 | ✅ 能,简洁高效 | ✅ 强烈推荐 |
所以:这个构造方法不是“可有可无”,而是“必不可少”!
少了它,你的代码连编译都通不过,更别说运行和得分了。
务必在手撕二叉树时,第一件事就是写出带参构造方法!
public void setRoot(TreeNode root) { this.root = root; // 将传入的节点设为当前树的根节点 }这个可以删吗?我只要核心代码
是的,setRoot方法在只考察“遍历与查找”核心逻辑的机考题中完全可以删除。下面详细说明原因和建议:
✅为什么可以删?
题目只要求“实现遍历与查找”
如果题目没有明确要求你“封装成完整的BinaryTree类”或“提供设置根节点的接口”,那么setRoot只是辅助测试用的,不属于核心算法逻辑。遍历和查找方法本身不依赖
setRoot
你的preOrder(node)、search(node, target)等方法都是以TreeNode为参数的,直接操作节点即可,根本不需要通过BinaryTree的root字段。老师批改关注的是算法实现
在人工阅卷时,老师主要看:- 遍历顺序是否正确(前/中/后序)
- 是否处理了空节点(
if (node == null) return;) - 查找逻辑是否完整
- 递归结构是否清晰
而不是你有没有提供setRoot这种“包装方法”。
✅更简洁的核心写法(推荐用于机考)
你可以完全不写BinaryTree类,直接把所有方法写成静态工具方法,或者只保留TreeNode+ 静态方法:
classTreeNode{intval;TreeNodeleft;TreeNoderight;publicTreeNode(intval){this.val=val;}}publicclassMain{// 前序遍历(静态方法,直接传入根节点)publicstaticvoidpreOrder(TreeNodenode){if(node==null)return;System.out.print(node.val+" ");preOrder(node.left);preOrder(node.right);}publicstaticvoidinOrder(TreeNodenode){if(node==null)return;inOrder(node.left);System.out.print(node.val+" ");inOrder(node.right);}publicstaticvoidpostOrder(TreeNodenode){if(node==null)return;postOrder(node.left);postOrder(node.right);System.out.print(node.val+" ");}publicstaticTreeNodesearch(TreeNodenode,inttarget){if(node==null)returnnull;if(node.val==target)returnnode;TreeNodeleft=search(node.left,target);if(left!=null)returnleft;returnsearch(node.right,target);}}✅ 这样写:
- 更聚焦核心算法;
- 减少冗余代码;
- 节省机考时间;
- 完全满足“手撕遍历与查找”的要求。
📌 结论
- 可以删:
setRoot不是遍历或查找的一部分,属于辅助方法。 - 建议删:在时间紧张的机考中,只写必要代码,避免画蛇添足。
- 关键保留:
TreeNode定义 + 三个遍历方法 + 查找方法 + 构造方法。
💡 记住:考试不是写工程代码,而是精准回答问题。题目要什么,你就写什么;不要的,果断舍弃!
✅ 所以,放心删掉setRoot,把精力放在确保遍历和查找逻辑 100% 正确上!