package com.areio.offer;
public class TreeNode {
//每一个结点要包含左指针,右指针,当前结点的值
int val;
TreeNode left = null;
TreeNode right = null;
//把左右节点设为null,构造新结点时不用自己设置
public TreeNode(int val){
this.val=val;
}
}
package com.areio.offer;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.LinkedList;
import java.util.List;
/*
* 前序遍历:根,左,右
* 中序遍历:左,根,右
* 后序遍历:左,右,根
* 层次遍历:第一层访问完再访问下一层
* */
public class _7A_TreeVisit {
/*创建一个完全二叉树
* 9 6 6` 5 16 2
*
* 9
* 6 6`
* 5 16 2
*
* 对非叶子结点建立其左右孩子,最后一个非叶子结点可能没右孩子,单独处理
* 完全二叉树有n/2个非叶子结点
*
* 前序:9 6 5 16 6` 2
* 中序:5 6 16 9 2 6`
* 后序:5 16 6 2 6` 9
* 层次:9 6 6` 5 16 2
* */
private static List<TreeNode> nodeList = null;
public static void createTree(int[] nums){
//将数组的元素全部转为结点
nodeList=new LinkedList();
for (int i=0;i<nums.length;i++){
nodeList.add(new TreeNode(nums[i]));
}
//建立前n/2-1个非叶子结点,最后一个单独处理
for (int i=0;i<nums.length/2-1;i++){
//i都是可以作为某子树根节点的元素
//构建左孩子
nodeList.get(i).left=nodeList.get(2*i+1);
//构建右孩子
nodeList.get(i).right=nodeList.get(2*i+2);
}
//单独处理最后一个非叶子结点
//处理左孩子(一定有)
int lastParentIndex=nums.length/2-1;
nodeList.get(lastParentIndex).left=nodeList.get(lastParentIndex*2+1);
//是否有右孩子?
if (nums.length%2==1){
nodeList.get(lastParentIndex).right=nodeList.get(lastParentIndex*2+2);
}
}
/*
* 前序递归版本
*
* 这是一个前序遍历的方法:先访问根,再访问完根的整颗左子树,最后访问根的整颗右子树;
* 每颗子树也需要按照前序遍历的方法——复用同个方法,只是子树的根换了而已
* */
public static void preOrder1(TreeNode root){
//递归结束条件
if (root==null){
return;
}
//1、访问根
System.out.print(root.val+" ");
//2、访问根的整颗左子树,左子树也按前序遍历方法来遍历
preOrder1(root.left);
//3、访问根的整颗右子树,右子树也按前序遍历方法来遍历
preOrder1(root.right);
}
/*
* 前序非递归版本
* 图解查看:https://blog.csdn.net/u012535132/article/details/84309406
* 将当前结点入栈,弹出结点并访问,再将弹出结点的右孩子,左孩子分别入栈;这样先出来的是左孩子
* 假设输入 9 6 6` 5 16 2,其先序遍历应该是9 6 5 16 6` 2
* */
public static void preOrder2(TreeNode root){
if (root==null) return;
Deque<TreeNode> stack=new ArrayDeque<>();
stack.push(root);
while (!stack.isEmpty()){
//出站并访问,将其右左结点入栈
TreeNode ele = stack.pop();
System.out.print(ele.val+" ");
if (ele.right!=null){
stack.push(ele.right);
}
if (ele.left!=null) {
stack.push(ele.left);
}
}
}
/*
* 中序递归版本
*
* 这是一个中序遍历的方法:先访问根的整颗左子树,再访问根,最后访问根的整颗右子树;
* 每颗子树也需要按照中序遍历的方法——复用同个方法,只是子树的根换了而已
* */
public static void inOrder1(TreeNode root){
//递归结束条件
if (root==null) return;
//1、先访问根的整颗左子树
inOrder1(root.left);
//2、访问根
System.out.print(root.val+" ");
//3、访问根的整颗右子树
inOrder1(root.right);
}
/*
* 中序非递归版本
* 图解查看:https://blog.csdn.net/u012535132/article/details/84329729
* 1、将某树/子树的root入栈,只要有左孩子就一直压入栈;
* 2、开始出栈并访问,判断其是否有右孩子,有则将这一个右孩子入栈(一旦一个元素加入栈,就需要先将其所有左节点入栈)
* 3、下一次出栈就是这个右孩子(作为根的身份),会去重复1、2、3
* 假设输入 9 6 6` 5 16 2,其中序遍历应该是5 6 16 9 2 6`
* */
public static void inOrder2(TreeNode root){
if (root==null) return;
Deque<TreeNode> stack=new ArrayDeque<>();
//p是当前结点
TreeNode p=root;
stack.push(root);
while (!stack.isEmpty()){
//只要有一个新元素入栈,就将其所有左孩子就全部入栈(左孩子全部遍历完才到它自己)
//1、左节点入栈
while (p!=null&&p.left!=null){
stack.push(p.left);
p=p.left;
}
//由于左孩子先访问,可以直接出栈并访问
//2、根(无论左右结点最终都会变成根)
p = stack.pop();
System.out.print(p.val+" ");
//3、右结点入栈
//判断当前出栈元素有没有右节点,只加入一个,将其所有的左孩子遍历完再到自己
if (p.right!=null){
stack.push(p.right);
p=p.right;
}else {
p=null;//标志没有新结点入栈,有的话下一次循环需要将其左节点全部入栈
}
}
}
/*
* 后序递归版本
*
* 这是一个后序遍历的方法:先访问根的整颗左子树,访问根的整颗右子树,最后再访问根;
* 每颗子树也需要按照后序遍历的方法——复用同个方法,只是子树的根换了而已
* */
public static void postOrder(TreeNode root){
//递归结束条件
if (root==null){
return;
}
//1、先访问根的整颗左子树
postOrder(root.left);
//2、访问根的整颗右子树
postOrder(root.right);
//3、访问根
System.out.print(root.val+" ");
}
/*
* 后序非递归:把所有左节点入栈,一个个出栈,只要其右节点没访问,当前结点就不可访问
* 1、把当前根的所有子节点入栈,
* 2、然后开始peek,判断peek的这个元素是否没有右子树或者右子树已被访问,
* 3、如果都是,那么可以出栈访问,
* 4、如果有右子树且右子树未访问,则把该右子树的根节点入栈,
* 5、重复1、2、3、4
*
*
* */
public static void postOrder2(TreeNode root){
if (root==null) return;
Deque<TreeNode> stack=new ArrayDeque<>();
//p是当前结点,pre是上次访问的结点
TreeNode p=root;
TreeNode pre=null;
//根先入栈
stack.push(p);
while (!stack.isEmpty()){
//只要有一个新元素入栈(没有新元素则后面会设置p=null),有左孩子,就把左孩子入栈
//1、左节点全部入栈
while (p!=null&&p.left!=null){
stack.push(p.left);
p=p.left;
}
//先读取栈顶元素,把每个元素当做根,判断其右孩子是否已访问,访问了才到自己
p=stack.peek();//①与中序的区别:先试探访问其右孩子是否已访问,后面才能出栈
//无右子树或右子树已被访问过,这个时候才可以出栈
if (p.right==null||p.right==pre) {//②与中序遍历的区别:右子树访问完才能到根
System.out.print(p.val+" ");
//3、访问根(无论左右结点最终都会变成根)
stack.pop();
pre=p;
p=null;//p=null标记没有新结点加入,则不需要加入新节点的左孩子,继续出栈,
}else {//有右节点且右节点还未被访问,则右孩子入栈
//2、右节点入栈
stack.push(p.right);
p=p.right;
}
}
}
/*
* 层次遍历:一个元素一出队列,就将其左右孩子入队
*
* */
public static void levelOrder(TreeNode root){
if (root==null) return;
Deque<TreeNode> queue = new ArrayDeque<>();
queue.offer(root);
//当前结点
TreeNode p=root;
while (!queue.isEmpty()){
//出队,与下面语句不可换,否则p会读到上次元素
p=queue.poll();
System.out.print(p.val+" ");
//把出队元素的左右孩子入队
if (p.left!=null)
queue.offer(p.left);
if (p.right!=null)
queue.offer(p.right);
}
}
public static void main(String[] args) {
//创建一个完全二叉树
createTree(new int[]{9,6,6,5,16,2});
System.out.println("前序递归:");
preOrder1(nodeList.get(0));
System.out.println("\n前序非递归:");
preOrder2(nodeList.get(0));
System.out.println("\n中序递归:");
inOrder1(nodeList.get(0));
System.out.println("\n中序非递归:");
inOrder2(nodeList.get(0));
System.out.println("\n后序递归:");
postOrder(nodeList.get(0));
System.out.println("\n后序非递归:");
postOrder2(nodeList.get(0));
System.out.println("\n层次非递归:");
levelOrder(nodeList.get(0));
// Deque<TreeNode> stack=new ArrayDeque<>();
// stack.push(new TreeNode(9));
// stack.push(new TreeNode(6));
// stack.push(new TreeNode(6));
// stack.push(new TreeNode(5));
// stack.push(new TreeNode(16));
// stack.push(new TreeNode(2));
//
// System.out.println(stack.peek().val);//2 读取最后进去的
// System.out.println(stack.peekLast().val);//9 读取最先进去的
// System.out.println(stack.pop().val);//2读取并删除最后进去的
}
}