1. 对于树的遍历有前序、中序、后序遍历,其中中序遍历对于一棵二叉搜索树来说得到的序列是一个上升的序列,因为树是递归定义的,所以我们在对树进行遍历的时候往往是采用递归的方式来进行处理的,而且使用递归的方式往往处理起来相对简洁一些但是有的情况下要求使用非递归方式来遍历一棵二叉搜索树,怎么样进行遍历呢?
我们知道递归遍历树的方式:对于树的访问先是从根节点开始进行的,递归左子树和右子树,对于左子树和右子树又可以看成是一棵子树继续递归下去,访问根节点的时候它不是直接处理根节点的而是需要等待它的左子树调用完之后那么才进行处理的,左子树也是一样,当访问到它的左子树的时候也需要等待它的左子树处理完毕才处理自己
这也就是先访问,后出现的特点,与一种数据结构的特点是类似的,那就是栈,栈也是 "先进后出" 的特点,其实递归来说其实就是一种隐式的栈,所以当我们采用非递归形式的时候我们可以自己声明一个栈,模仿递归求解的过程来进行处理
2. 以下面的二叉搜索树为例:
10
8 12
5 9 11 15
3
① 首先要声明一个栈,因为在压栈和进栈的元素是树的节点,所以元素的类型为TreeNode(树节点的定义),递归求解的时候访问10,10等待8的元素处理完再进行处理,8也是一样等待5处理完再进行处理一直到树的最左端3这个时候便遇到递归的出口那么就返回了,在这个过程中我们需要将访问到的元素进行进栈,栈中的元素为10 8 5 3(栈底到栈顶),其中这个过程要使用到循环,循环的条件为当前的指针c指向的节点不为空,当c指向3的左子树的时候为空那么为空退出循环
② 循环退出来之后这个时候而且栈不为空的情况下就需要把栈顶元素弹出,指针c指向这个元素3的右子树继续循环此时c为空了,但是是不是就不执行了呢?不是,栈中的元素不为空那么我们需要继续进行处理,这个时候又需要最外层的循环来表示,
外层循环的条件为c指向的元素不为空而且栈中的元素不为空,这个时候不进入里层循环,弹出元素5,指针c指向元素5的右子树为空继续上面的过程把8弹出来,然后指向8的右子树不为空,把9加进栈中,指针c指向9, 9的左右子树都为空那么直接弹出,指针c这个时候为空那么弹出10,c指向10的右子树12不为空指向12,由于12的左子树不为空那么进入里层循环,把12,11加进来...然后继续上面的循环弹出11,12,加入15弹出15,这个时候指针c指向的元素为空而且栈中的元素为空那么退出循环
可以使用Consumer类中的accept来接收从栈中弹出来的元素,形成的序列为3 5 8 9 10 11 12 15 是有序的,与中序遍历的结果是完全一样的
代码如下:
import java.util.Stack;
import java.util.function.Consumer;
/* 10
8 12
5 9 11 15
3
*/
public class Main<K, V> {
//构建一棵二叉搜索树
public static void main(String[] args) {
TreeNode<Integer> root = new TreeNode<Integer>(10);
TreeNode<Integer> l = new TreeNode<Integer>(8);
TreeNode<Integer> r = new TreeNode<Integer>(12);
TreeNode<Integer> ll = new TreeNode<Integer>(5);
TreeNode<Integer> lr = new TreeNode<Integer>(9);
TreeNode<Integer> rl = new TreeNode<Integer>(11);
TreeNode<Integer> rr = new TreeNode<Integer>(15);
TreeNode<Integer> lll = new TreeNode<Integer>(3);
root.left = l;
root.right = r;
l.left = ll;
l.right = lr;
r.left = rl;
r.right = rr;
ll.left = lll;
//调用者消费元素
inOrder(root, k->{
System.out.print(k + " ");
});
}
//Consumer相当于集合中的List
public static void inOrder(TreeNode<Integer> root, Consumer<Integer> con){
if(root == null) return;
TreeNode<Integer> c = root;
Stack<TreeNode> stack = new Stack<TreeNode>();
//当栈不为空的时候或者指针指向的内容不为空的时候那么继续循环
while(c != null || !stack.isEmpty()){
//当c不为空的时候那么生产节点
while(c != null){
stack.push(c);
c = c.left;
}
//当c不为空的时候那么消费元素
if(!stack.isEmpty()){
TreeNode<Integer> poll = stack.pop();
con.accept(poll.val);
c = poll.right; //有可能为空
}
}
}
}
TreeNode定义如下:
public class TreeNode<T> {
public T val;
public TreeNode<T> left = null;
public TreeNode<T> right = null;
public TreeNode<T> parent = null;
public TreeNode(T val) {
this.val = val;
}
@Override
public String toString() {
return "TreeNode [val=" + val + "]";
}
}