最近有小伙伴面试的时候被问到写循环遍历二叉树,虽然我面了几次还没被问到相关的,但是想着也是该会的东西,就花了点时间写了点代码,废话不多说,上菜。
我的二叉树的声明如下:
这个节点是在offer这个包里面的,所以下面的代码都会 import offer.TreeNode
package offer;
/**
* @author WangPan
* @date 2019/2/25 19:44
* @description
**/
public class TreeNode {
public int val;
public TreeNode left;
public TreeNode right;
public TreeNode(int x){ val = x;}
}
前序遍历
我一般对三种遍历方式的记法就是看“根”的位置,遍历的前中后其实就是对应的根节点的遍历顺序,前序遍历即:根左右。
思路:循环遍历,这里使用一个栈,栈先入后出,因此循环先弹出栈顶节点,再将该栈顶节点的右节点和左节点入栈。代码如下:
package binaryTree;
import java.util.Stack;
import offer.TreeNode;
/**
* @author WangPan
* @date 2019/4/20 17:01
* @description 二叉树的前序遍历,非递归版本
**/
public class preorder {
public static void pre(TreeNode root){
if(root == null)
return;
Stack<TreeNode> stack = new Stack<>();
stack.push(root);
while (!stack.isEmpty()){
TreeNode temp = stack.pop();
System.out.println(temp.val);
//每出栈一个,依次把右左子点入栈
if(temp.right != null)
stack.push(temp.right);
if(temp.left != null)
stack.push(temp.left);
}
}
}
中序遍历
遍历方式:左根右
思路:仍然使用栈来保存节点,中序遍历要求先访问到最左节点,因此先要将left路径上所有元素依次入栈,再进行出栈操作,如果栈顶元素的右节点不为空,则将其右节点入栈,对其右子树进行中序遍历。
package binaryTree;
import java.util.Stack;
import offer.TreeNode;
/**
* @author WangPan
* @date 2019/4/20 19:08
* @description 二叉树的中序遍历,非递归版本
**/
public class inorder {
public static void in(TreeNode root){
if(root == null)
return;
Stack<TreeNode> stack = new Stack<>();
stack.push(root);
while (!stack.isEmpty()){
//首先让这个节点的所有左节点一次入栈
while (root.left != null){
stack.push(root.left);
root = root.left;
}
TreeNode temp = stack.pop();
System.out.println(temp.val);
//出栈节点的右节点如果不是null则要进栈,对其右子树进行中序遍历
if(temp.right != null){
root = temp.right;
stack.push(root);
}
}
}
}
后序遍历
遍历方式:左右根
思路1:因为仍然要先从左开始遍历,所以大框架根中序遍历较相似,这里使用两个栈来实现,一个主栈保存遍历到的节点,一个辅助栈来保存暂时不输出的“根节点”,一个难点就是输出辅助栈中的根节点时候的判断条件,具体请看代码。该方法将使用两个栈,空间复杂度大一点。
package binaryTree;
import java.util.Stack;
import offer.TreeNode;
/**
* @author WangPan
* @date 2019/4/20 19:33
* @description 二叉树的后序遍历,利用两个栈,一个主栈一个辅助栈,当两个栈栈顶元素相同时输出,在中序遍历的基础上进行改进
**/
public class postorder {
public static void post(TreeNode root){
if(root == null)
return;
Stack<TreeNode> stack = new Stack<>();
//stack1用来保存那些暂时不输出的根节点
Stack<TreeNode> stack1 = new Stack<>();
stack.push(root);
while (!stack.isEmpty()){
while (root.left != null){
stack.push(root.left);
root = root.left;
}
TreeNode temp = stack.peek();
//右节点为null时这个节点可以输出,无论其是左节点还是右节点
if(temp.right == null){
System.out.println(stack.pop().val + " ");
//当temp是右节点时候要把根节点也输出,输出的条件是两个栈的栈顶元素相同
while(!stack1.isEmpty() && stack.peek() == stack1.peek()){
System.out.print(stack1.pop().val + " ");
stack.pop();
}
} else {
stack1.push(temp);//根节点要进入stack1
root = temp.right;
stack.push(root);
}
}
}
}
}
思路2:使用一个栈保存节点,当要输出的时候判断其是否满足可输出条件,即为叶子节点或其子节点已输出完毕,使用变量pre来保存前一个输出的节点,具体请看代码。
package binarytree;
import java.util.Stack;
import offer.TreeNode;
/**
* @author WangPan
* @date 2019/4/20 19:33
* @description 二叉树的后序遍历,非递归版本,利用一个栈,在前序遍历的基础上修改
**/
public class PostOrder {
//使用一个栈来访问,思路与前序遍历相似,前序遍历时根节点直接输出,这里先判断是该节点的子节点是否输出过,如果输出过或为叶子节点则可以直接输出,否则将其右左子节点一次入栈
public static void postOneStack(TreeNode root){
if(root == null) {
return;
}
Stack<TreeNode> stack = new Stack<>();
TreeNode pre = null;
stack.push(root);
while (!stack.isEmpty()){
TreeNode temp = stack.peek();
if((temp.left == null && temp.right == null)
|| (pre != null && (pre == temp.left || pre == temp.right))) {
System.out.print(stack.pop().val + " ");
pre = temp;
}else {
if(temp.right != null) {
stack.push(temp.right);
}
if(temp.left != null) {
stack.push(temp.left);
}
}
}
}
}
总结
循环遍历二叉树的方法可能会在面试中问到,当然也是作为一名程序员应该掌握的东西,希望能对大家有点帮助,如有不当之处还请指出,一起学习,一起进步。我的GitHub主页:WangPanHUST