在上一篇文章(二叉树的遍历( 一 ))中,我们通过思考函数入栈出栈的过程,理解了递归遍历二叉树的原理,下面我们来理解非递归的写法。
在递归遍历中,我们发现,程序首先把根节点的左’子孙’依次入栈,所以我们可以有如下代码:
public void traverse1(TreeNode root){
if(root == null) return;
Stack<TreeNode> stack = new Stack<>();
TreeNode nodeee = root;
while(node != null){
//visit 先序 stack.push(node);
node = node.left;
}
然后,如果栈不为空,依次出栈,看其是否有右子节点,如果有右节点,执行上一步操作:
while (stack.size() > 0) {
node = stack.pop();
//visit 中序 if (node.right != null) {
node = node.right;
while (node != null) {
stack.push(node);
node = node.left;
}
}
}
}
那么,先序遍历的visit点,就在stack.push()
之前;中序遍历的visit点,就在stack.pop()
之后。
上面这两段代码可以优化为下面一个循环:
public static void traverse2(TreeNode root){
if(root == null) return;
Stack<TreeNode> stack = new Stack<>();
TreeNode node = root;
while(!(node==null && stack.empty())){
while(node != null){
//visit 先序 stack.push(node);
node = node.left;
}
node = stack.pop();
//visit 中序 node = node.right;
}
}
这样就实现了非递归的先序遍历和中序遍历。
还有另一种方式实现非递归的先序遍历,用了深度优先搜索(DFS)的思想,其实,二叉树的先序遍历,中序遍历,后序遍历,都是深度优先搜索。
深度优先搜索:
首先,把根节点入栈。 ①
如果栈不为空,弹出栈顶元素。 ②
把该元素的子节点入栈。 ③
循环②,③,直到栈为空。④
那么,我们只要保证右节点先于左入栈,就能实现”根->左->右“的先序遍历。
代码如下:
public static void traverse3(TreeNode root){
Stack<TreeNode> stack = new Stack<>();
stack.push(root);
while(!stack.empty()){
TreeNode node = stack.pop();
//visit 先序 if(node.right != null){
stack.push(node.right);
}
if(node.left != null) {
stack.push(node.left);
}
}
}
从这个思想出发,我们可以很简单的实现另一种遍历:”根->右->左“遍历。
虽然这种遍历没有名字,但是他是后序遍历的反序。所以我们可以利用两个栈,利用栈的LIFO特点,来实现后续遍历。
代码如下:
public static void traverse4(TreeNode root){
Stack<TreeNode> stack = new Stack<>();
Stack<TreeNode> result = new Stack<>();
stack.push(root);
while(!stack.empty()){
TreeNode node = stack.pop();
//visit 根->右->左 result.push(node);
if(node.left != null) {
stack.push(node.left);
}
if(node.right != null){
stack.push(node.right);
}
}
while(!result.empty()){
node = result.pop();
//visit 后序 }
}
那么,怎么用一个栈来实现后序遍历呢?
下面的文章会讲到。
为什么我们模拟程序栈的时候,没有后续遍历的visit点呢?
这主要是因为后序遍历是非尾递归的,深层的原因,之后可能会讲到。
咳咳,欢迎关注,欢迎点赞,欢迎评论哦~~