本次采用的例子树:
int[] pre = {1, 2, 4, 7, 3, 5, 6, 8}; //前序遍历序列
int[] in = {4, 7, 2, 1, 5, 3, 8, 6}; //中序遍历序列
package com.xxx;
import java.util.*;
/**
* create by ziqiiii
*/
public class Test {
//Definition for binary tree
public static class TreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode(int x) {
val = x;
}
}
static public void main(String[] args) {
int[] pre = {1, 2, 4, 7, 3, 5, 6, 8}; //前序遍历序列
int[] in = {4, 7, 2, 1, 5, 3, 8, 6}; //中序遍历序列
//根据前序遍历序列,中序遍历序列,构建树。 (注意,能构建树必须要有中序遍历)
TreeNode h = reConstructBinaryTree(pre, in);
System.out.print("层次遍历:");
levelOrder(h);
System.out.println("\n======");
int high = treeHigh(h);
System.out.print("树高:" + high);
System.out.println("\n======");
System.out.print("递归实现前序遍历:");
preOrder(h);
System.out.println();
System.out.print("递归实现中序遍历:");
inOrder(h);
System.out.println();
System.out.print("递归实现后序遍历:");
postOrder(h);
System.out.println("\n======");
System.out.print("迭代实现前序遍历:");
preOrder2(h);
System.out.println();
System.out.print("迭代实现中序遍历:");
inOrder2(h);
System.out.println();
System.out.print("迭代实现后序遍历:");
postOrder2(h);
System.out.print("迭代实现三种遍历:");
System.out.println("\n======");
preOrder3(h);
System.out.println();
inOrder3(h);
System.out.println();
postOrder3(h);
}
//根据前序遍历序列,中序遍历序列,递归构建树。 (注意,能构建树必须要有中序遍历)
//这个构建树的例子,是参照《剑指offer》(重建二叉树)递归实现
//具体可参考:https://blog.csdn.net/qq_20417499/article/details/97013639
static public TreeNode reConstructBinaryTree(int[] pre, int[] in) {
int preLen = pre.length;
int inLne = in.length;
if (preLen == 0 || inLne == 0 || preLen != inLne) {
return null;
}
int f = pre[0];
TreeNode root = new TreeNode(f);
int leftNum = 0;
for (int i = 0; i < inLne; i++) {
if (in[i] == f) {
break;
}
leftNum++;
}
int rightNum = preLen - 1 - leftNum;
int[] leftpre = new int[leftNum];
int[] rightpre = new int[rightNum];
int j = 1;
for (int m = 0; m < leftNum; m++) {
leftpre[m] = pre[j++];
}
for (int m = 0; m < rightNum; m++) {
rightpre[m] = pre[j++];
}
int[] leftin = new int[leftNum];
int[] rightin = new int[rightNum];
j = 0;
for (int m = 0; m < leftNum; m++) {
leftin[m] = in[j++];
}
j += 1;
for (int m = 0; m < rightNum; m++) {
rightin[m] = in[j++];
}
root.left = reConstructBinaryTree(leftpre, leftin);//递归,将左子树的前序、中序遍历序列,构建左子树
root.right = reConstructBinaryTree(rightpre, rightin);
return root;
}
//层次遍历
static void levelOrder(TreeNode t) {
if (t != null) {
Queue<TreeNode> queue = new LinkedList<>();
queue.add(t);
while (!queue.isEmpty()) {
TreeNode tn = queue.poll();
System.out.print(tn.val);
if (tn.left != null) {
queue.add(tn.left);
}
if (tn.right != null) {
queue.add(tn.right);
}
}
}
}
//查询树高
static int treeHigh(TreeNode t) {
if (t == null) {
return 0;
}
int leftTreeHigh = treeHigh(t.left);
int rightTreeHigh = treeHigh(t.right);
int high = leftTreeHigh > rightTreeHigh ? leftTreeHigh + 1 : rightTreeHigh + 1;
return high;
}
//递归实现
static void preOrder(TreeNode root) {
if (root != null) {
System.out.print(root.val);
preOrder(root.left);
preOrder(root.right);
}
}
static void inOrder(TreeNode root) {
if (root != null) {
inOrder(root.left);
System.out.print(root.val);
inOrder(root.right);
}
}
static void postOrder(TreeNode root) {
if (root != null) {
postOrder(root.left);
postOrder(root.right);
System.out.print(root.val);
}
}
//迭代实现前序遍历,借助stack栈
static void preOrder2(TreeNode t) {
Stack<TreeNode> stack = new Stack<>();
while (t != null || !stack.isEmpty()) {
while (t != null) {
System.out.print(t.val);
stack.push(t);
t = t.left;
}
if (!stack.isEmpty()) {
t = stack.pop();
t = t.right;
}
}
}
//迭代实现中序遍历, 只需要修改print的时间
static void inOrder2(TreeNode t) {
Stack<TreeNode> stack = new Stack<>();
while (t != null || !stack.isEmpty()) {
while (t != null) {
stack.push(t);
t = t.left;
}
if (!stack.isEmpty()) {
t = stack.pop();
System.out.print(t.val);
t = t.right;
}
}
}
// 迭代实现后序遍历, 根前面的前序,中序遍历不一样,
/*
* 因为根节点是最先处理的,但要最后输出,所以可用将各个节点入栈2次,当节点出栈时栈顶与出栈节点的值不一样时,打印输出。
*
* 总思路就是把根、右子树、左子树都压入栈中2次
* 每次循环释放一个栈中元素,如果释放的和栈顶元素相同证明没有被操作过~~
* 就分别将其右子树和左子树都压入栈中。
*/
static void postOrder2(TreeNode t) {
if (t != null) {
Stack<TreeNode> stack = new Stack<>();
stack.push(t);// 压入两次
stack.push(t);
while (!stack.isEmpty()) {
t = stack.pop(); // 释放一个
if (!stack.isEmpty() && t == stack.peek()) { // 即没有被操作过
if (t.right != null) {
stack.push(t.right);
stack.push(t.right);
}
if (t.left != null) {
stack.push(t.left);
stack.push(t.left);
}
} else {
System.out.print(t.val);// 即已经操作过一次
}
}
}
}
/*
* (1)层序遍历与其他三种(前序、中序和后序遍历)的主要区别是:
* 层次遍历通过各结点一次性获得其左右儿子结点信息并借助队列以自顶向下的顺序,形成按宽度优先方式遍历二叉树。
* 而其他三种遍历均是各结点借助堆栈的逆序特点,按自底向上的顺序逐步处理左右儿子,形成按深度优先方式遍历二叉树。
*
* (2)如果将层序遍历程序中的队列直接改为堆栈,同时将入栈(原来是入队)顺序改为先右儿子再左儿子,后根,其遍历输出结果就是前序遍历。(可用递归实现)
*
* (3)对于中序遍历和后序遍历,关键是要判别左子树和右子树什么时候遍历结束。
* 如果将层序遍历程序中的队列改为堆栈,同时仍然按照层次遍历的整体控制思路,对出入栈操作做些修改,仍然可以实现中序遍历和后序遍历。
* 要点是:在原来程序控制过程中,结点出队后是将其左右儿子入队;
* 现在可将控制过程改为:结点出栈后,将当前结点“加塞”在其左右儿子中再次入栈。
* 如果“加塞”在左右儿子之间再次入栈就是中序遍历,如果“加塞”在左右儿子之前再次入栈就是后序遍历。
* 也就是每个结点做两次出入栈处理(类似后序遍历的非递归程序):
* a.中序遍历要点:结点出栈时,如果是第一次出栈,按照右儿子、当前结点、左儿子顺序入栈,
* 即左儿子在栈顶,当前结点此时是第二次入栈。将来如果第二次出栈则直接输出。
* b.后序遍历要点:过程与上述中序遍历一样,唯一区别是入栈顺序为:当前结点、右儿子、左儿子。
* */
//此方法参考:https://blog.csdn.net/u014787113/article/details/49717831
//迭代实现前序遍历: 循环处理:(先打印根,再处理左子树,将右子树进栈),直到处理完全部左子树,再将右子树出栈处理
static void preOrder3(TreeNode t) {
if (t != null) {
Stack<TreeNode> stack = new Stack<>();
while (true) {
while (t != null) {
System.out.print(t.val);
if (t.right != null) {
stack.push(t.right);
}
t = t.left;
}
if (stack.isEmpty()) {
break;
}
t = stack.pop();
}
}
}
//此方法参考:https://blog.csdn.net/u014787113/article/details/49717831
static void inOrder3(TreeNode t) {
if (t != null) {
Stack<TreeNode> stack = new Stack<>();
while (true) {
while (t != null) {
stack.push(t);
t = t.left;
}
if (stack.isEmpty()) {
break;
}
TreeNode tn = stack.pop();
System.out.print(tn.val);
t = tn.right;
}
}
}
//此方法参考:https://blog.csdn.net/u014787113/article/details/49717831
static void postOrder3(TreeNode t) {
if (t != null) {
Stack<TreeNode> stack = new Stack<>();
stack.push(t);
while (!stack.isEmpty()) {
if ((stack.peek().left != t) && (stack.peek().right != t)) {
TreeNode tn = stack.peek();
while (tn != null) {
if (tn.left != null) {
//当前节点有左孩子
if (tn.right != null) {
//如果当前节点有右孩子优先让右孩子进入
stack.push(tn.right);
}
stack.push(tn.left);
} else {
stack.push(tn.right);
}
tn = stack.peek();
}
//将最后放进去的空节点弹出
stack.pop();
}
t = stack.pop();
System.out.print(t.val);
}
}
}
}
运行输出:
层次遍历:12345678
======
树高:4
======
递归实现前序遍历:12473568
递归实现中序遍历:47215386
递归实现后序遍历:74258631
======
迭代实现前序遍历:12473568
迭代实现中序遍历:47215386
迭代实现后序遍历:74258631
======
12473568
47215386
74258631
后序遍历 非递归实现:
非递归遍历中,后序遍历相对更难实现,因为需要在遍历完左右子节点之后,再遍历根节点,因此不能直接将根节点出栈。这里使用一个 last 指针记录上次出栈的节点,当且仅当节点的右孩子为空(top->right == NULL),或者右孩子已经出栈(top->right == last),才将本节点出栈:
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> list = new ArrayList();
Stack<TreeNode> stack = new Stack();
TreeNode t = root;
TreeNode last = null; //记录上一次处理的节点
while(true){
while(t != null){
stack.push(t);
t = t.left;
}
if(!stack.isEmpty()){
TreeNode top = stack.peek(); //栈顶节点
if(top.right == null || top.right == last){
//没有右孩子,或者右孩子已经处理过了
stack.pop(); //则这个节点以下的子树是全部处理过了,弹出栈
list.add(top.val);
last = top; //记录处理的节点
}else{
t = top.right; //右孩子还没被访问过
}
}else{
break;
}
}
return list;
}
}