Binary Tree Postorder Traversal

本文介绍了如何非递归地进行二叉树后序遍历。首先,解释了后序遍历的定义,即先遍历左子树,然后右子树,最后访问根节点。接着,提供了两种迭代解决方案:第一种利用栈,根据节点的左右子树状态决定何时输出节点值;第二种通过逆序操作,先访问根节点,再右子树,最后左子树,利用栈进行节点存储和顺序调整,遍历完后反转链表得到正确顺序。

题目

Given a binary tree, return the postorder traversal of its nodes' values.

For example:
Given binary tree {1,#,2,3},

   1
    \
     2
    /
   3

return [3,2,1].

Note: Recursive solution is trivial, could you do it iteratively?

要求求二叉树后序遍历结果,且是非递归的算法。

解题思路

   二叉树后序遍历是指根结点相对于左右子树节点的遍历顺序,后序遍历访问顺序是左子树、右子树根结点。对于这个题目有两种解法,第一种是方法是:用栈来存储待访问的结点,如果栈非空,弹出一个节点,对于这个结点,如果它的左子树和右子树均为空,则肯定输出该节点信息;如果左子树不为空但右子树为空,当左子树已经访问(这里的访问表示已经输出值),下一个访问的结点肯定就是该节点;如果左子树为空但右子树不为空,当右子树已经访问,下一个访问的结点肯定也是该节点;如果左子树右子树均不为空,访问顺序肯定就是左子树、右子树和该节点,所以如果右子树已经访问,则下一个访问的结点肯定也是该节点;对于其他情况,该节点右子树不为空,则右结点压栈,左子树不为空,则左结点压栈。因为栈的特殊性,所以压栈顺序是相反的。因此,当访问一个节点时,我们记录该节点,如果下次碰到该节点的父节点,就通过判断来访问父节点。于是,有了下面的代码(不清楚的建议手动模拟一下):

public List<Integer> postorderTraversal(TreeNode root) {
	List<Integer> list = new ArrayList<Integer>();
	if (root == null) {
		return list;
	}
	Stack<TreeNode> stack = new Stack<TreeNode>();
	TreeNode preNode = null;
	TreeNode curNode = null;
	stack.push(root);
	while (!stack.isEmpty()) {
		curNode = stack.peek();
		if ((curNode.left == null && curNode.right == null)
				|| (preNode != null && (curNode.left == preNode || curNode.right == preNode))) {
			preNode = curNode;
			list.add(curNode.val);
			stack.pop();
		} else {
			if (curNode.right != null) {
				stack.push(curNode.right);
			}
			if (curNode.left != null) {
				stack.push(curNode.left);
			}
		}
	}
	return list;
}

第二种思路可能更难想到,算法比较简单容易理解,对于后序遍历的左子树、右子树和根结点的顺序,逆向顺序就是根结点、右子树和左子树。因此,栈来存储待访问的结点,如果栈非空,弹出一个节点,对于这个结点,直接将它的值加入链表,如果左子树非空,左结点入栈;右子树非空,右结点入栈。遍历完毕,将链表反转返回即可。

public List<Integer> postorderTraversal2(TreeNode root) {
	List<Integer> list = new ArrayList<Integer>();
	if (root == null) {
		return list;
	}
	Stack<TreeNode> stack = new Stack<TreeNode>();
	TreeNode curNode = null;
	stack.push(root);
	while (!stack.isEmpty()) {
		curNode = stack.pop();
		list.add(curNode.val);
		if (curNode.left != null) {
			stack.push(curNode.left);
		}
		if (curNode.right != null) {
			stack.push(curNode.right);
		}			
	}
	Collections.reverse(list);
	return list;
}
对于上面两种算法,在leetcode里均通过,但第一种所用时间短一些。








### 插入一系列数值到空二叉搜索树后的后续遍历序列 插入一系列数值 `{5, 2, 7, 3, 4, 1, 6}` 到一个初始为空的二叉搜索树 (Binary Search Tree),可以按照如下方式构建该树。对于每一个新节点,将其与当前子树根节点比较:如果小于根节点,则插入左子树;否则插入右子树[^1]。 #### 构建过程 - 首先插入 `5` 作为根节点。 - 接着插入 `2`,由于它小于 `5`,因此成为其左孩子。 - 然后插入 `7`,大于 `5`,故成为其右孩子。 - 继续插入 `3`,它位于 `5` 的左侧区域,并且也大于现有的 `2` 节点,于是成为 `2` 的右孩子。 - 下一步插入 `4`,同样进入 `5` 左侧部分,在此之后进一步深入至 `2` 右边并找到位置于 `3` 的右侧。 - 插入 `1`,它是最小值,最终会到达最左边的位置即 `2` 的左侧。 - 最终插入 `6`,定位在 `7` 的左侧。 完成上述操作后形成的二叉搜索树结构如下: ```plaintext 5 / \ 2 7 / \ \ 1 3 6 \ 4 ``` #### 后序遍历定义 后序遍历是一种深度优先遍历方法,遵循访问顺序为 **左 -> 右 -> 根**。 #### 计算后序遍历序列 基于以上树形结构执行后序遍历算法可得结果为: - 开始从左下角叶子结点依次向上回溯处理各分支直到整棵树完全被覆盖为止。 具体路径表现为 `(1->3->4->2)->(6->7->5)` 即 `[1, 3, 4, 2, 6, 7, 5]`. 因此所求解的答案就是这个列表形式表示的结果集。 ```python def post_order_traversal(root): result = [] def traverse(node): if node is None: return # Traverse Left Subtree traverse(node.left) # Traverse Right Subtree traverse(node.right) # Visit Node result.append(node.value) traverse(root) return result ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值