文章目录
- 非递归实现二叉树前、中、后序遍历
- 一、非递归实现前序遍历
- 1.思路
- 2.代码
- 二、非递归实现二叉树的中序遍历
- 1.思路
- 2.代码
- 三、非递归实现二叉树的后序遍历
- 1.思路
- 2.代码
非递归实现二叉树前、中、后序遍历
一、非递归实现前序遍历
1.思路
-
前序遍历的顺序是 :根——左——右
-
模拟递归在栈上的执行过程
-
用栈来实现
1.用cur代替root的移动,将当前cur压栈并打印
2.cur指向当前cur的左子树,cur.left压栈并打印,直到cur的左边遇到空为止
3.先序遍历时,根节点压栈先不着急取出,栈是先进后出的,先找根的左子树,后续会出栈来利用根节点找右子树,栈维护了根节点出现的顺序
4.当cur为空时,弹出栈顶结点,栈顶结点的左边是为空的cur,要利用栈顶结点来找他的右边
5.如果此时栈顶元素的右边也为空,该节点的根左右找完了,当前结点代表着上一个根结点的左树找完了
6.上一个根节点的根、左找完了,弹出栈顶元素(上一个结点),来找他的右边
- 也就是说,先让cur来找根并打印,一直找下去,遇到空了找右边。当最小的左子树的根左右都找齐后,这个子树和它的上一级的根已经找到,需要开始找当前根的右边,形成循环。最后这个二叉树的左子树找齐后,开始找右子树。
- 用两个while循环,小循环的条件是cur不为空,负责进栈,大循环嵌套小循环,小循环外负责出栈
- 大循环的条件是:出栈结点的右边不为空,或者栈不为空
2.代码
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<>();
if (root==null){
return res;
}
TreeNode cur = root;//cur指向root
Deque<TreeNode> stack = new LinkedList<>();
while (cur != null || !stack.isEmpty()) {
while (cur != null) {//cur不等于空,进栈并打印
stack.push(cur);
// System.out.print(cur.val + " ");
res.add(cur.val);
cur = cur.left;//先找左树所有的根
}
TreeNode top = stack.pop();//cur为空了,出栈找结点的右边
cur = top.right;//cur指向结点的右边,如果cur不为空,进入大循环,再次进小循环入栈
//如果cur==null,则判断栈空不空,栈不为空,进入大循环,不进小循环,只出栈
}
return res;
}
二、非递归实现二叉树的中序遍历
1.思路
1.和上面的前序遍历类似,不过中序遍历打印的顺序是: 左——根——右
2.所以在小循环中,cur找到左树的根后,进栈时不打印,等出栈时再打印
3.也就是说,找到叶子结点后,叶子结点左边为空时,弹出叶子结点并打印(左、根)
4.找叶子结点的右边,右边为空时,说明左中右已找完,
5.此时左边已打印完,cur为空,出栈并打印根节点,利用根节点找右边的结点
- 打印的位置与前序遍历不同
2.代码
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<>();
if (root==null){
return res;
}
TreeNode cur = root;//cur指向root
Deque<TreeNode> stack = new LinkedList<>();
while (cur != null || !stack.isEmpty()) {
while (cur != null) {//cur不等于空,进栈并打印
stack.push(cur);
// System.out.print(cur.val + " ");
cur = cur.left;//先找左树所有的根
}
TreeNode top = stack.pop();//cur为空了,出栈找结点的右边
res.add(top.val);
cur = top.right;//cur指向结点的右边,如果cur不为空,进入大循环,再次进小循环入栈
//如果cur==null,则判断栈空不空,栈不为空,进入大循环,不进小循环,只出栈
}
return res;
}
三、非递归实现二叉树的后序遍历
1.思路
-
后序遍历的具体思路类似,但是有不同的地方
-
前序遍历中,找结点的右边要出栈找,维护出栈顺序,出栈的那一刻起,左边之前已经为空或者打印了,出栈取到结点找到右边,如果有结点新的结点会进栈,如果为空说明子树应该找完了,此时跟出栈的结点无关了
-
中序遍历只是打印的位置不一样,前序和中序遍历,要看结点的右边,需要出栈来查看,出栈的目的是,根和左已经找完了,只需要取出来找右边就可以了,右边找完根据栈返回上一级
1.后续遍历的顺序: 左——右——根
2.因为根是最后打印的,先要看左节点,所以根节点先不能出栈,用peek取到 存的根节点,找根节点的右边
3.右边为空时,打印栈顶的结点,弹出栈顶,cur为空,进入大循环,查看栈顶的右边,不为空,改变cur的指向
4.出栈有要求,所以会造成peek元素过后,再次找到已经打印的结点
5.所以在打印的时候还要判断是否打印过该节点
2.代码
public void postorderNor(TreeNode root) {
if (root == null) {
return;
}
TreeNode cur = root;//cur指向root
TreeNode prev = null;//记录打印过的结点
Deque<TreeNode> stack = new LinkedList<>();
while (cur != null || !stack.isEmpty()) {
while (cur != null) {
cur = cur.left;//先找左树所有的根
}
TreeNode top = stack.peek();//根节点不出栈,用peek取到根节点
if (top.right==null||top.right ==prev ){//排除打印过的元素
//左边找到了,右边为空,打印栈中的结点
System.out.print(top.val+" ");
stack.push(top);//打印完后弹出
//此时cur为空
prev = top;//记录打印过的结点
}else {
cur = top.right;//右边不为空,cur指向右边
}
}
}
点击移步博客主页,欢迎光临~