/*
* 二叉树的非递归遍历,前序中序还是挺简单的,可是后序遍历就需要一个pre来记录前一个指针了,我老是忘记要怎么写后序非递归。
* 今天参考了https://blog.youkuaiyun.com/u012975705/article/details/80258664的博客,自己就规划化三种非递归遍历,写完之后
* 就觉得后序遍历不用pre,变得超级简单了,而且三种遍历很相似,完全可以比较记忆,还是自己的想法不够好。代码能跑通,也可以和上一篇
* 结合起来看待。
*
*
*
*/
import java.util.*;
//结点定义
class TreeNode {
int data;
TreeNode left, right;
public TreeNode(int data) {
this.data = data;
this.left = null;
this.right = null;
}
}
//测试
public class Solution {
public static void main(String args[]) {
int preOrder[] = new int[]{10, 3, 1, 2, 9, 4, 8, 5, 7, 6};
int inOrder[] = new int[]{1, 3, 2, 10, 4, 9, 5, 8, 6, 7};
//调用建树的函数
Solution s = new Solution();
TreeNode root = s.buildTree(preOrder, 0, preOrder.length - 1, inOrder, 0, inOrder.length - 1);
s.preTravel(root);
s.inTravel(root);
s.postTravel(root);
}
//前序非递归,这个是最简单的二叉树非递归遍历 顺序是DLR
public void preTravel(TreeNode root) {
if (root == null)
return;
//保存结果用
ArrayList<Integer> res = new ArrayList<>();
Stack<TreeNode> stack = new Stack<>();
TreeNode cur = root;
while(!stack.empty()||cur!=null){
//左孩子一直入栈,并且一个左孩子的后继节点还是左孩子,所以顺序地保存为结果
while(cur!=null){
stack.push(cur);
res.add(cur.data);
cur=cur.left;
}
//弹出最左边的孩子
cur=stack.pop();
cur=cur.right; //cur必要时为空,不然死循环
}
System.out.println("前序:" + res.toString());
}
//中序非递归,最常用到
public void inTravel(TreeNode root) {
if (root == null)
return;
//保存结果
ArrayList<Integer> res = new ArrayList<>();
Stack<TreeNode> stack = new Stack<>();
TreeNode cur=root;
//目标是得到LDR,所以先找到最左孩子
while(!stack.empty()||cur!=null){
//找到最左孩子
while(cur!=null){
stack.push(cur);
cur=cur.left;
}
cur =stack.pop();
res.add(cur.data);
cur=cur.right;
}
System.out.println("中序:"+res.toString());
}
//后序非递归,最难,在之前的一篇博客写过设置pre的方法,在这里更新一种方法,根据前序DLR,我们只要稍作改变就可以得到DRL是不?
//再把DRL翻转一下,就得到了后序LRD了,这种方法可以和前序比较记忆,所以强烈推荐这么做,这样一来其实后序就有两种遍历方法了
public void postTravel(TreeNode root) {
if (root == null)
return;
//保存结果
ArrayList<Integer> res = new ArrayList<>();
Stack<TreeNode> stack = new Stack<>();
TreeNode cur=root;
//目标是得到DRL,最后结果再翻转为LRD
while(!stack.empty()||cur!=null){
//仿照前序遍历,一个孩子的后继节点为他的右孩子,不断找到最右孩子,并且记录路径和结果
while(cur!=null){
stack.push(cur);
res.add(cur.data);
cur=cur.right;
}
//弹出最右界结点
cur=stack.pop(); //配合前序写这个简单绝配!!!!妈妈再也不用担心写不来后序遍历了
cur=cur.left;
}
//一定记得翻转
Collections.reverse(res);
System.out.println("后序:"+res.toString());
}
//根据前序和中序建立一颗二叉树
public TreeNode buildTree(int preOrder[], int preStart, int preEnd, int inOrder[], int inStart, int inEnd) {
//根节点
if (preStart > preEnd || inStart > inEnd)
return null;
TreeNode root = new TreeNode(preOrder[preStart]);
//空树,递归出口
if (preStart == preEnd || inStart == inEnd)
return root;
//找到根节点在中序中的位置,以便划分成两部分
int loc = locateRoot(preOrder[preStart], inOrder);
root.left = buildTree(preOrder, preStart + 1, loc - inStart + preStart, inOrder, inStart, loc - 1);
root.right = buildTree(preOrder, loc - inStart + preStart + 1, preEnd, inOrder, loc + 1, inEnd);
return root;
}
//找到结点在中序遍历的位置,帮助建树
public int locateRoot(int key, int inOrder[]) {
for (int i = 0; i < inOrder.length; i++)
if (inOrder[i] == key)
return i;
return -1;
}
}