面试题2——遍历二叉树
题目
用先序,中序,后序,分别遍历一个二叉树
思想
1.(递归法)利用递归思想,向左向右遍历一个二叉树
2.(非递归)利用栈和循环,将结点压栈,按不同遍历方式选择不同策略从栈中取出结点输出
PS:后序遍历思想,从根结点开始,将所有最左结点全部压栈,每当一个结点出栈时,都先扫描该结点的右子树,只有当一个结点的左孩子和右孩子结点均被访问过了,才能访问结点自身。(判断当右孩子输出时,则向上回溯)
前序递归遍历算法:访问根结点-->递归遍历根结点的左子树-->递归遍历根结点的右子树
中序递归遍历算法:递归遍历根结点的左子树-->访问根结点-->递归遍历根结点的右子树
后序递归遍历算法:递归遍历根结点的左子树-->递归遍历根结点的右子树-->访问根结点
实现
二叉树结点 BinaryTreeNode.java
//二叉树节点 public class BinaryTreeNode { public int data; public BinaryTreeNode left; public BinaryTreeNode right; public BinaryTreeNode(int data, BinaryTreeNode left, BinaryTreeNode right){ super(); this.data = data; this.left = left; this.right = right; } }
遍历 BinaryTreeTraversing.java
package lms.datastructure.tree; import javafx.scene.transform.Rotate; import java.util.Stack; //遍历二叉树 public class BinaryTreeTraversing { /** 1 / \ 2 3 / \ / \ 4 5 6 7 / \ 8 9 \ 10 */ //====先序遍历========= //递归 先序 public static void preOrderRecur(BinaryTreeNode root) { if (null != root) { System.out.print(root.data + " "); preOrderRecur(root.left); preOrderRecur(root.right); } } //非递归 先序 public static void preOrder(BinaryTreeNode root) { Stack<BinaryTreeNode> stack = new Stack<BinaryTreeNode>(); while (true) { while (root != null) { System.out.print(root.data + " "); stack.push(root); root = root.left; } if (stack.isEmpty()) break; root = stack.pop(); root = root.right; } } //=====中序遍历============ //递归 中序 public static void inOrderRecur(BinaryTreeNode root) { if (null != root) { inOrderRecur(root.left); System.out.print(root.data + " "); inOrderRecur(root.right); } } //中序遍历 非递归 public static void inOrder(BinaryTreeNode root) { Stack<BinaryTreeNode> stack = new Stack<>(); while (true) { //循环,遍历左边 while (root != null) { stack.push(root); root = root.left; } if (stack.isEmpty()) break; //无右叶子的时候,从栈中弹出 root = stack.pop(); System.out.print(root.data + " "); //无右叶子的时候,从栈中弹出 root = root.right; } } //=====后序遍历============ //递归 后序 public static void postOrderRecur(BinaryTreeNode root) { if (null != root) { postOrderRecur(root.left); postOrderRecur(root.right); System.out.print(root.data + " "); } } //非递归 后序(核心思想:每次左子树遍历完之后,每一步判断是否右子树为null,不为null指针移动到右子树上继续进行 “左->右->根” 步骤) public static void postOrder(BinaryTreeNode root) { Stack<BinaryTreeNode> stack = new Stack<>(); while (true) { if (null != root) { stack.push(root); root = root.left; } else { if (stack.isEmpty()) return; //到叶子节点 判断如果是叶子节点(是否为右孩子结点),则输出 if (null == stack.lastElement().right) { root = stack.pop(); System.out.print(root.data + " "); //右子树右叶子访问之后向上回溯 while (root == stack.lastElement().right) { System.out.print(stack.lastElement().data + " "); root = stack.pop(); //System.out.print(root.data + " "); if (stack.isEmpty()) break; } } //移动指针到节点的右子树上 if (!stack.isEmpty()) { root = stack.lastElement().right; } else { //终止条件 root = null; } } } } public static void main(String[] args) { /** 1 / \ 2 3 / \ / \ 4 5 6 7 / \ 8 9 \ 10 */ BinaryTreeNode node10 = new BinaryTreeNode(10, null, null); BinaryTreeNode node8 = new BinaryTreeNode(8, null, null); BinaryTreeNode node9 = new BinaryTreeNode(9, null, node10); BinaryTreeNode node4 = new BinaryTreeNode(4, null, null); BinaryTreeNode node5 = new BinaryTreeNode(5, node8, node9); BinaryTreeNode node6 = new BinaryTreeNode(6, null, null); BinaryTreeNode node7 = new BinaryTreeNode(7, null, null); BinaryTreeNode node2 = new BinaryTreeNode(2, node4, node5); BinaryTreeNode node3 = new BinaryTreeNode(3, node6, node7); BinaryTreeNode node1 = new BinaryTreeNode(1, node2, node3); System.out.println("====先序遍历===="); preOrderRecur(node1); System.out.println(""); System.out.println("====先序遍历非递归===="); preOrder(node1); System.out.println(""); System.out.println("====中序遍历===="); inOrderRecur(node1); System.out.println(""); System.out.println("====中序遍历非递归===="); inOrder(node1); System.out.println(""); System.out.println("====后序遍历===="); postOrderRecur(node1); System.out.println(""); System.out.println("====后序遍历非递归===="); postOrder(node1); System.out.println(""); } }
后序遍历——双栈法
后序遍历的顺序:左孩子 -> 右孩子 -> 根结点。打印每个结点需要访问根结点三次,先从根结点开始找到左孩子,返回再找到右孩子,再返回到根结点,需要访问根结点三次,直接按照当前顺序进行遍历不知如何下手,那么我们可以换一个角度去考虑。不妨借助一个栈先存储 根结点 -> 右孩子 -> 左孩子 的结果,再将其依次弹出就是后序遍历的顺序了。
将前序遍历的逻辑改写为:弹出每个栈顶结点作为当前结点并存储到一个辅助栈中,如果当前结点有左孩子就先压入左孩子,如果有右孩子就后压入右孩子,每次取栈顶弹出到辅助栈中。最后将得到的辅助栈中元素依次弹出得到的就是后序遍历的结果。
//后序遍历 非递归 双栈法 public static void postOrderTwoStack(BinaryTreeNode root) { Stack<BinaryTreeNode> stack1 = new Stack<>(); // 辅助栈,存储 根 -> 右 -> 左 的结果 Stack<BinaryTreeNode> stack2 = new Stack<>(); if (root != null) { stack1.push(root); while (!stack1.isEmpty()) { root = stack1.pop(); stack2.push(root); if (root.left != null) { stack1.push(root.left); // 有左孩子就先压入左孩子 } if (root.right != null) { stack1.push(root.right); // 有右孩子就后压入右孩子 } } while (!stack2.isEmpty()) { System.out.print(stack2.pop().data + " "); // 逆序打印 根 -> 右 -> 左 的结果,就是后序遍历的结果 } } }
输出结果
====先序遍历====
1 2 4 5 8 9 10 3 6 7
====先序遍历非递归====
1 2 4 5 8 9 10 3 6 7
====中序遍历====
4 2 8 5 9 10 1 6 3 7
====中序遍历非递归====
4 2 8 5 9 10 1 6 3 7
====后序遍历====
4 8 10 9 5 2 6 7 3 1
====后序遍历非递归====
4 8 10 9 5 2 6 7 3 1