大家好,我是Kaiqisan,是一个已经走出社恐的一般生徒,都说所有的递归都可以使用非递归的方式来解决,所以这次来一起康康非递归版本的二叉树的遍历
递归的本质就是不断往栈中塞入待执行代码,然后在代码块被执行的时候就会被调用执行,直到栈空,所以我们遍历树的时候也需要利用栈结构。
众所周知,前中后序遍历树的方法都很好理解
前序遍历就是先访问自己,再访问左节点最后访问右节点(中左右)
中序遍历就是先访问左节点,再访问自己最后访问右节点(左中右)
后序遍历就是先访问左节点,再访问右节点最后访问自己(左右中)
所以在递归方法遍历的时候就改变一下打印的时机就可以了
所以在转到非递归方法的时候也需要利用这种特点
非递归先序
先让一头结点入栈,然后再开始迭代,每次迭代之前判断一下是否栈空(栈空表示遍历结束)
每次遍历都先把当前出栈节点的右节点入栈,再把当前节点的左节点入栈,为什么要先让右节点先入栈呢?这样可以让先前左节点先出栈,从而实现先序遍历的中左右顺序
static void searchTree(Tree head) {
if (head != null) {
Stack<Tree> stack = new Stack<>();
stack.add(head); // 元素入栈
while (!stack.isEmpty()) {
head = stack.pop(); // 元素出栈
System.out.print(head.val + " ");
// 先压右后压左
if (head.right != null) {
stack.push(head.right);
}
if (head.left != null) {
stack.push(head.left);
}
}
}
}
以这棵树为例
* 1
* / \
* 2 3
* / \ / \
* 4 5 6 7
*
下面为每次执行的流程(右边为栈头,优先出栈)
stack: 1 print: ""
stack: 32 print: "1" (1出栈,1的左右孩子入栈)
stack: 354 print: "12" (同理)
stack: 35 print: "124" (4无子节点,只是单方面出栈)
stack: 3 print: "1245"
stack: 76 print: "12453"
stack: 7 print: "124536"
stack: print: "1245367" (栈无元素,结束遍历)
非递归中序
思路:从根节点开始,先无脑遍历左孩子,到头之后,出栈一个元素,回到当前节点未遍历的父节点,然后遍历右孩子,然后接着重复一开始的操作,开始无脑遍历左孩子…
这样就可以实现中序遍历的(左中右)
static void searchTree(Tree head) {
if (head != null) { // 只是判断一个数是否为空树
Stack<Tree> stack = new Stack<>();
while (!stack.isEmpty() || head != null) {
if (head != null) {
stack.push(head); // 入栈
head = head.left;
} else {
head = stack.pop(); // 出栈
System.out.println(head.val);
head = head.right;
}
}
}
}
以这棵树为例
* 1
* / \
* 2 3
* / \ / \
* 4 5 6 7
*
下面为每次执行的流程(右边为栈头,优先出栈)
stack: 1 print: ""
stack: 12 print: ""
stack: 124 print: "" (4无左节点,也没有右节点,所以通过出栈节点回到没有被遍历的父节点2)
stack: 12 print: "4"
stack: 15 print: "42"
stack: 3 print: "4251"
stack: 36 print: "4251"
stack: 3 print: "42516"
stack: 7 print: "425163"
stack: print: "4251637"
非递归后序
使用了两个栈,先使用和先序遍历一样的思路来出入栈(假设先入栈1),但是入栈是时候不同于上面的先右后左,这次是先左后右,把栈1出来的元素再放入栈2,最后再把所有栈2的元素
倒出,依次输出。
为什么这么做,仔细看上面的先序和后序的遍历顺序,先序是中左右,后序是左右中,所以先使用中右左的遍历方法,然后再利用第二个栈来倒出元素,把所有的元素翻转,于是就变成了后序的左右中
static void searchTree4(Tree head) {
if (head != null) {
Stack<Tree> stack1 = new Stack<>();
Stack<Tree> stack2 = new Stack<>();
stack1.push(head);
while (!stack1.isEmpty()) {
head = stack1.pop();// 出栈
stack2.push(head); // 入栈
// 先压左再压右
if (head.left != null) {
stack1.push(head.left);
}
if (head.right != null) {
stack1.push(head.right);
}
}
// 倒元素
while (!stack2.isEmpty()) {
System.out.print(stack2.pop().val);
}
}
}
如果只用一个栈的方法目前还没想到,敬请期待!
总结
要注意的点还是tmd开头那句话,所有的递归都可以使用非递归的方式来解决,如果在入职考试中写算法题的时候,切记不要使用递归,而且在工程中也是很少使用递归的。