二叉树打印
题目: 构建一个二叉树,分别使用先、中、后序进行树的遍历打印
如图所示,是二叉树的基本结构。对于此树先、中、后序的遍历结果如图中所示的样子。
示例代码:
-
树的节点结构
public class TreeNode { int val; TreeNode left; TreeNode right; } -
构建插入节点元素操作
/** * @param root 父节点 * @param val 插入的值 * @return 返回插入节点的父节点 */ private TreeNode insertRecursive(TreeNode root, int val) { if (root == null) { root = new TreeNode(val); return root; } if (val < root.val) { root.left = insertRecursive(root.left, val); // 插入到左节点 }else{ root.right=insertRecursive(root.right,val); // 插入到有节点 } return root; } -
遍历打印
/** * 遍历打印 */ public void inorderTraversal(){ inorderTraversalRecursive(root); } private void inorderTraversalRecursive(TreeNode root) { if(root!=null){ System.out.println(root.val); //先序遍历 inorderTraversalRecursive(root.left); System.out.println(root.val); //中序遍历 inorderTraversalRecursive(root.right); System.out.println(root.val); //后序遍历 } }
可以看到在代码段 inorderTraversalRecursive 方法当中,只需要调整打印语句的位置就可以分别实现,先、中、后序的打印。
那么有趣的是,为什么只通过调整打印语句的顺序就可以实现,不同的遍历呢?其主要依赖的是递归调用的递归序。
二叉树的递归调用图示
通过图中的顺序可以看到,在执行 inorderTraversalRecursive 函数方法的时候,每一个节点都没重复调用的 三次,而这三次的出现的时机,分别如图所示:
关于树中某一节点元素左右交集的关系
题目:给出树中某一个元素 X ,关于 X 元素所在的树,分别进行先序与后序遍历,关于 X 节点之前的元素与之后的元素,两个去一个交集。而交集的元素代表的含义是什么?
先看图中的结果 【交集的节点有且全部都是该节点的 祖先节点】
哪为什么交集一定就是祖先节点呢?
- 从图中可以看到,一棵树整体由三部分构成,分别是紫色的左子树,黄色右子树,以及红色色的根节点。
- 先序遍历则表示,三种颜色出现的顺序为 红色->紫色->黄色
- 后序遍历则表示,三种颜色的出现顺序为 紫色->黄色->红色
- 因此对于 E 元素来说,其先序遍历黄色元素一定在右边而红色在左边,后序遍历黄色与红色都在右边。所以取交集,能取到的就只有黑色部分,也就是一定包含根节点。
- 同样的原理套用在旁边的以 B 节点为根节点的子树上,一样适用,也就保证 E 元素的先后序遍历当中,左右两边分别各有根节点 B,也就保证了 B 节点一定取得到交集。
- 这样就解答了上面
为什么交集一定就是祖先节点的问题。
非递归遍历树
之前遍历树都是通过递归的方式,分别在递归序的不同时期打印输出,实现树的遍历。那么如果不同递归的方式可以实现树的遍历吗?
首先思考一下,递归的原理是什么?压栈! 既然是压栈那我们是否可以模拟递归的压栈方式,从而实现树的遍历呢?
用栈实现树的遍历
- 先序遍历【利用栈先右后左的顺序进行压栈进行】
-
代码示例
/** * 利用栈完成先序遍历 * @param node 根节点 */ public static void preorderTraversal(TreeNode node){ if(node!=null){ Stack<TreeNode> stack=new Stack<TreeNode>(); stack.push(node); while (!stack.empty()){ node=stack.pop(); System.out.println(node.val); if(node.right!=null){ stack.push(node.right); } if(node.left!=null){ stack.push(node.left); } } } }
代码附录
- 树节点元素
package com.peppa.binaryTree;
/**
* 二叉树的结构定义
*/
public class TreeNode {
int val;
TreeNode left;
TreeNode right;
public TreeNode(int val) {
this.val = val;
this.left = null;
this.right = null;
}
public int getVal() {
return val;
}
public void setVal(int val) {
this.val = val;
}
public TreeNode getLeft() {
return left;
}
public void setLeft(TreeNode left) {
this.left = left;
}
public TreeNode getRight() {
return right;
}
public void setRight(TreeNode right) {
this.right = right;
}
@Override
public String toString() {
return "TreeNode{" +
"val=" + val +
", left=" + left +
", right=" + right +
'}';
}
}
- 树结构与构建
package com.peppa.binaryTree;
/**
* 二叉树类
*/
public class BinaryTree {
TreeNode root;
public BinaryTree() {
this.root = null;
}
public void insert(int val) {
root = insertRecursive(root, val);
}
/**
* @param root 父节点
* @param val 插入的值
* @return 返回插入节点的父节点
*/
private TreeNode insertRecursive(TreeNode root, int val) {
if (root == null) {
root = new TreeNode(val);
return root;
}
if (val < root.val) {
root.left = insertRecursive(root.left, val); // 插入到左节点
}else{
root.right=insertRecursive(root.right,val); // 插入到有节点
}
return root;
}
/**
* 遍历打印
*/
public void inorderTraversal(){
inorderTraversalRecursive(root);
}
private void inorderTraversalRecursive(TreeNode root) {
if(root!=null){
System.out.println(root.val); //先序遍历
inorderTraversalRecursive(root.left);
// System.out.println(root.val); //中序遍历
inorderTraversalRecursive(root.right);
// System.out.println(root.val); //后序遍历
}
}
}
- 压栈实现先序遍历
package com.peppa.binaryTree;
import java.util.Stack;
/**
* 利用压栈的方式进行树的遍历
*/
public class PushBinaryTree {
/**
* 利用栈完成先序遍历
* @param node 根节点
*/
public static void preorderTraversal(TreeNode node){
if(node!=null){
Stack<TreeNode> stack=new Stack<TreeNode>();
stack.push(node);
while (!stack.empty()){
node=stack.pop();
System.out.println(node.val);
if(node.right!=null){
stack.push(node.right);
}
if(node.left!=null){
stack.push(node.left);
}
}
}
}
}