二叉树前序、中序、后序遍历——Java实现

本文详细介绍了二叉树的前序、中序和后序遍历算法,包括递归和非递归实现方式。通过具体示例,展示了如何使用栈来辅助非递归遍历过程,特别强调了后序遍历的双栈技巧。

在非递归实现中,需要借助栈来实现。前序和中序比较类似,也简单一些,但是后序遍历需要两个栈来进行辅助,稍微复杂一些。
在这里插入图片描述
前序遍历:先遍历根结点,然后遍历左子树,最后遍历右子树
4 2 1 3 6 5 7 8
中序遍历:先遍历左子树,然后遍历根结点,最后遍历右子树
1 2 3 4 5 6 7 8
后序遍历:先遍历左子树,然后遍历右子树,最后遍历根结点
1 3 2 5 8 7 6 4

1、递归实现
class TreeNode{
	int val = 0;
	TreeNode left = null;
	TreeNode right = null;
	public TreeNode(int val) {
		this.val = val;
	}
}

public class OrderTree {
	//前序遍历(递归实现)
	public static void preOrderRecur(TreeNode node) {
		if(node == null) {
			return;
		}
		System.out.print(node.val + ",");
		preOrderRecur(node.left);
		preOrderRecur(node.right);
	}
	//中序遍历(递归实现)
	public static void inOrderRecur(TreeNode node) {
		if(node == null) {
			return;
		}
		inOrderRecur(node.left);
		System.out.print(node.val + ",");
		inOrderRecur(node.right);
	}
	//后序遍历(递归实现)
	public static void posOrderRecur(TreeNode node) {
		if(node == null) {
			return;
		}
		posOrderRecur(node.left);
		posOrderRecur(node.right);
		System.out.print(node.val + ",");
	}
	public static void main(String args[]) {
		TreeNode root = new TreeNode(4);
		root.left = new TreeNode(2);
		root.left.left = new TreeNode(1);
		root.left.right = new TreeNode(3);
		root.right = new TreeNode(6);
		root.right.left = new TreeNode(5);
		root.right.right = new TreeNode(7);
		root.right.right.right = new TreeNode(8);
		System.out.println("前序遍历递归实现:");
		preOrderRecur(root);
		System.out.println();
		System.out.println("中序遍历递归实现:");
		inOrderRecur(root);
		System.out.println();
		System.out.println("后序遍历递归实现:");
		posOrderRecur(root);
	}
}
2、非递归实现
import java.util.Stack;

class TreeNode{
	int val = 0;
	TreeNode left = null;
	TreeNode right = null;
	public TreeNode(int val) {
		this.val = val;
	}
}

public class OrderTree {
	//前序遍历(非递归实现)
	public static void preOrderUnRecur(TreeNode node) {
		if(node == null) {
			return;
		}
		Stack<TreeNode> s = new Stack<>();
		s.push(node);
		while(! s.isEmpty()) {
			TreeNode temp = s.pop();
			System.out.print(temp.val + ",");
			if(temp.right != null) {
				s.push(temp.right);
			}
			if(temp.left != null) {
				s.push(temp.left);
			}
		}
	}
	//中序遍历(非递归实现)
	public static void inOrderUnRecur(TreeNode node) {
		if(node == null) {
			return;
		}
		Stack<TreeNode> s = new Stack<>();
		while(! s.isEmpty() || node != null) {
			if(node != null) {
				s.push(node);
				node = node.left;
			}else {
				node = s.pop();
				System.out.print(node.val + ",");
				node = node.right;
			}
		}
	}
	//后序遍历(非递归实现)
	public static void posOrderUnRecur(TreeNode node) {
		if(node == null) {
			return;
		}
		Stack<TreeNode> s1 = new Stack<>();
		Stack<TreeNode> s2 = new Stack<>();
		s1.push(node);
		while(! s1.isEmpty()) {
			node = s1.pop();
			s2.push(node);
			if(node.left != null) {
				s1.push(node.left);
			}
			if(node.right != null) {
				s1.push(node.right);
			}
		}
		while(! s2.isEmpty()) {
			System.out.print(s2.pop().val + ",");
		}
	}
	public static void main(String args[]) {
		TreeNode root = new TreeNode(4);
		root.left = new TreeNode(2);
		root.left.left = new TreeNode(1);
		root.left.right = new TreeNode(3);
		root.right = new TreeNode(6);
		root.right.left = new TreeNode(5);
		root.right.right = new TreeNode(7);
		root.right.right.right = new TreeNode(8);
		System.out.println("前序遍历非递归实现:");
		preOrderUnRecur(root);
		System.out.println();
		System.out.println("中序遍历非递归实现:");
		inOrderUnRecur(root);
		System.out.println();
		System.out.println("后序遍历非递归实现:");
		posOrderUnRecur(root);
	}
}
<think>首先,用户的问题是:“我想查找二叉树前序、中后序遍历结果相同的处理方法。请问二叉树前序后序遍历结果相同的处理方法?” 我需要基于用户提供的引用内容来回答这个问题。用户的引用包括: - 引用[1]:讨论了如何从前序遍历后序遍历,或者从中后序遍历前序遍历。 - 引用[2]:定义了前序、中后序层次遍历的顺。 - 引用[3]:展示了用代码从前序构建二叉树,或从中后序构建二叉树。 - 引用[4]:展示了二叉树遍历JavaScript代码示例。 用户的问题核心是:当前序、中后序遍历结果相同时,如何处理这种情况。 回想二叉树遍历定义: - 前序遍历:根节点 -> 左子树 -> 右子树 - 中遍历:左子树 -> 根节点 -> 右子树 - 后序遍历:左子树 -> 右子树 -> 根节点 用户想知道当这三种遍历列相同时的处理方法。 在正常情况下,二叉树前序、中后序遍历列是不同的,因为它们访问节点的顺不同。只有当二叉树的结构特殊时,这些列才可能相同。 考虑一个二叉树的结构: - 如果二叉树是空的,那么所有遍历列都是空的,所以相同。但这可能不是用户的意思。 - 更实际的是,如果二叉树只有一个节点(根节点),那么前序、中后序遍历都只有一个节点,所以列相同。 - 另一个例子:如果二叉树的所有节点都只有右子树,没有左子树(即一条向右的链)。 - 例如:根 -> 右孩子 -> 右孙子 -> ... - 前序遍历:根、右孩子、右孙子...(顺是先根,然后右子树) - 中遍历:根、右孩子、右孙子...(因为左子树为空,所以根后立即右子树) - 后序遍历:右孙子、右孩子、根...(先访问右子树,最后根) 在这种情况下,列是不同的。中前序在某些结构中相似,但一般来说不同。 仔细想想: - 前序:根、左、右 - 中:左、根、右 - 后序:左、右、根 要使所有三种列相同,必须满足:根节点访问时,左子树右子树都不存在,或者整个树的结构简化。 事实上,只有当二叉树没有分支时,即每个节点最多有一个孩子时,列可能在某些情况下匹配。 具体来说: 1. 空树:所有遍历列为空。 2. 单节点树:所有列相同,只有一个元素。 3. 对于更复杂的树:假设每个节点只有右孩子(或只有左孩子)。 - 只有右孩子的树: - 前序:根、右子树(列是根、右孩子、右孙子...) - 中:左子树为空,所以根、然后右子树(列根、右孩子、右孙子...)——与前序相同。 - 后序:右子树、然后根(列右孙子、右孩子、根...)——与前序不同。 - 只有左孩子的树: - 前序:根、左子树(根、左孩子、左孙子...) - 中:左子树、根(左孙子、左孩子、根...)——与前序不同。 - 后序:左子树、根(左孙子、左孩子、根...)——与中相同,但与前序不同。 所以,在只有右孩子的情况下,前序相同,但后序不同。在只有左孩子的情况下,中后序相同,但前序不同。 要使所有三种列相同,必须是每个节点没有两个孩子,并且列顺一致。实际上,只有一个节点时相同。 另一个可能性:如果二叉树是完全不平衡的,但列相同的情况很少见。 我回忆起:对于一棵二叉树,当前序相同,或者中后序相同时,树的结构有特定形式。 但用户问的是前序、中后序所有三种相同时的处理方法。 在极端情况下: - 如果树为空,列相同(空)。 - 如果树只有一个节点,列相同。 - 如果树有多个节点,但所有节点都只有右孩子(斜树),那么: - 前序:A,B,C(如果A是根,B是右孩子,C是右孙子) - 中:A,B,C(因为左为空) - 后序:C,B,A 这里前序相同,但后序不同。 类似地,对于只有左孩子的斜树: - 前序:A,B,C(A根,B左孩子,C左孙子) - 中:C,B,A - 后序:C,B,A 所以前序不同,中后序相同。 要使所有三种相同,必须没有子树,或者列中节点顺相同。但基于遍历,只有当树只有根节点时,列才完全相同。 考虑一个更小的例子:根一个左孩子。 - 节点:A(根),B(左孩子) - 前序:A,B - 中:B,A - 后序:B,A 列不同。 类似地,根一个右孩子: - 前序:A,B - 中:A,B - 后序:B,A 列不同(前序相同,但后序不同)。 因此,二叉树的节点数大于1时,前序、中后序列不可能完全相同,因为它们的遍历强制不同的列顺(根在开始、中间或结束)。 结论:对于二叉树前序、中后序遍历列只有在树为空或只有一个节点时才相同。 那么,处理方法就是识别这种情况:如果遍历列相同,那么树要么是空的,要么只有一个节点。 在编程中,处理这种情况很简单: - 如果输入列为空,返回空树。 - 如果列只有一个元素,创建一个单节点树返回。 在构建二叉树的上下文中,如用户引用中,给出遍历列求树结构时,如果列相同,则树是退化或简单的。 在用户的引用[1][3]中,展示了如何从前序或从中后序遍历列构建二叉树。 如果列相同,意味着树只有一个节点或空。 所以,在算法中,可以添加检查: - 如果列长度小于等于1,直接返回。 例如,在引用[3]的代码中: ```cpp TreeNode *buildTree(vector<int> &preorder, vector<int> &inorder) { if (preorder.empty()) { // 空节点 return nullptr; } // 如果列长度为1,创建一个节点返回 // 代码中,preorder[0]是根,然后分割... // 但代码没有显式处理长度1,但递归会处理,因为当左子树大小为0时,pre1in1为空,递归返回nullptr。 } ``` 在递归中,当列长度为1时,它会正确处理:创建节点,左子树右子树递归调用时,因为left_size=0,所以pre1in1为空,buildTree返回nullptr,类似pre2in2如果右子树为空。 所以在标准算法中,列相同的情况自然被处理,因为列长度为0或1时,树为空或单节点。 用户的问题是“处理方法”,所以我应该解释当列相同时,树的结构简单,并建议在算法中添加检查或理解其含义。 另外,用户可能误解了,或者有特殊情况。 在引用中,没有直接提到列相同的情况,所以我基于一般知识回答。 回答结构: 1. 解释当二叉树前序、中后序遍历列相同时,这意味着树只有根节点或为空。 2. 在构建二叉树的算法中,这种情况已经隐含处理,因为递归基础情况处理空列或单元素列。 3. 给出简单示例或代码片段。 4. 生成相关问题。 最后,添加引用标识。 系统指令要求:使用中文回答;生成相关问题;添加引用标识。 引用来源是用户提供的[^1], [^2], [^3], [^4]。 在我的回答中,需要自然地添加引用,例如当提到遍历定义时引用[^2],当提到构建算法时引用[^1]或[^3]。 生成相关问题。 完整回答: </think>### 二叉树前序、中后序遍历结果相同的处理方法 在二叉树中,前序遍历(根节点 -> 左子树 -> 右子树)、中遍历(左子树 -> 根节点 -> 右子树)后序遍历(左子树 -> 右子树 -> 根节点)的列通常不同,因为它们访问节点的顺存在本质差异[^2]。只有当二叉树的结构极其特殊时,这三种遍历列才可能完全相同。以下是针对这种情况的处理方法: 1. **列相同时的二叉树结构分析**: - **空树情况**:如果二叉树为空(节点数为0),所有遍历列均为空列([]),因此相同。处理方法:直接返回空树(`nullptr` 或类似表示)。 - **单节点树情况**:如果二叉树只有一个根节点(无左子树右子树),所有遍历列均为单元素列(例如 `[A]`),因此相同。处理方法:创建单节点树返回。 - **多节点树情况**:对于节点数 ≥ 2 的二叉树前序、中后序列不可能完全相同。因为: - 前序遍历根节点在列开头。 - 中遍历根节点在列中间。 - 后序遍历根节点在列末尾。 例如,对于有两节点的树(根一个左孩子),前序为 `[A,B]`,中为 `[B,A]`,后序为 `[B,A]`——列不同。类似地,斜树(所有节点只有右孩子或左孩子)也无法满足所有列相同[^1][^2]。 2. **编程实现中的处理方法**: - 在构建二叉树的算法中(如从前序列重建),列相同的情况通常已隐含处理。标准递归算法会自然处理空列或单元素列作为基础情况。以下是基于引用[^1][^3]的优化处理: - **检查列长度**:在递归函数开头,添加显式检查,避免不必要的递归。 - **示例代码(Java风格,基于引用[^1])**: ```java public TreeNode buildTree(int[] preorder, int[] inorder) { if (preorder.length == 0) { // 空树情况 return null; } if (preorder.length == 1) { // 单节点树情况 return new TreeNode(preorder[0]); } // 如果列相同(长度相同且元素全等),直接创建单节点树 boolean allSame = true; for (int i = 0; i < preorder.length; i++) { if (preorder[i] != inorder[i]) { // 假设postorder相同,但通常需额外检查 allSame = false; break; } } if (allSame) { return new TreeNode(preorder[0]); // 列相同,返回单节点树 } // 否则,正常递归构建(基于前序) int rootVal = preorder[0]; int rootIndex = 0; while (inorder[rootIndex] != rootVal) { rootIndex++; } // 分割左子树右子树列 int[] leftPreorder = Arrays.copyOfRange(preorder, 1, 1 + rootIndex); int[] leftInorder = Arrays.copyOfRange(inorder, 0, rootIndex); int[] rightPreorder = Arrays.copyOfRange(preorder, 1 + rootIndex, preorder.length); int[] rightInorder = Arrays.copyOfRange(inorder, rootIndex + 1, inorder.length); TreeNode left = buildTree(leftPreorder, leftInorder); TreeNode right = buildTree(rightPreorder, rightInorder); return new TreeNode(rootVal, left, right); } ``` - **关键点**: - 显式处理列长度 ≤1 的情况,提高效率。 - 如果输入列长度相同且元素全等,直接返回单节点树(因为列相同隐含树结构简单)。 - 正常列时,使用递归分割(前序根确定根值,中定位根索引)[^1][^3]。 3. **实际应用注意事项**: - **列有效性验证**:在输入列时,先验证长度是否一致。如果长度不一致,说明列无效(遍历列长度应相同)。引用[^2]强调遍历定义的一致性。 - **性能优化**:对于列相同的情况,跳过递归直接返回,减少时间复杂度(从 $O(n^2)$ 优化到 $O(1)$)。 - **边界场景**:在空树或单节点树中,列相同是正常行为,无需特殊处理。但如果输入列相同且长度 >1,应检查数据错误(例如列生成 bug)。 - **JavaScript 示例**:引用[^4]展示了遍历代码,类似地,可在遍历函数中添加前置检查: ```javascript function buildTree(preorder, inorder) { if (preorder.length === 0) return null; if (preorder.length === 1) return new Node(preorder[0]); if (JSON.stringify(preorder) === JSON.stringify(inorder)) { return new Node(preorder[0]); // 列相同,返回单节点 } // ...正常递归构建 } ``` 总结:二叉树前序、中后序遍历列相同仅当树为空或单节点时出现。处理方法是通过显式检查列长度内容,直接返回对应树结构,避免冗余计算。这适用于二叉树构建算法(如 LeetCode 相关问题),并确保鲁棒性[^1][^3]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值