树的生成见上一篇博客
后序遍历:
1.遍历左边节点,到左叶子节点后返回父节点,切换到右子树
2.遍历右边结点,遍历完后回到父节点
记忆:左右根
例子(注意,这里的树和我创建的树不一样!!!):
代码实现:
后序遍历的递归实现
public static void postOrderRecursion(TreeNode T) {
if (T == null) {
return;
}
postOrderRecursion(T.lchild);
postOrderRecursion(T.rchild);
System.out.print(T.value + " ");
}
后序遍历的非递归实现:
实现思路:由于是左右中的遍历顺序,当遍历完一个子树后需要判断(若为左子树则切换到右子树,若为右子树则切换到左子树)。新建一个栈来保存结点的状态。当结点不为空时,遍历其左子树直到最左结点,切换到右子树,再遍历右子树中各个结点的左子节点,循环。到了左叶子节点或右子树的叶子节点,则栈二顶部为right,弹出栈一顶部的结点值。
public static void postOrderNormal(TreeNode T) {
//后序遍历
int left = 1;//在辅助栈里表示左节点
int right = 2;//在辅助栈里表示右节点
Stack<TreeNode> stack1 = new Stack<>();
Stack<Integer> stack2 = new Stack<>();//辅助栈,用于判断子节点返回值
while (T != null || !stack1.isEmpty()) {
while (T != null) {
//将节点压住栈1,并在栈2将节点标记为左节点
//若到了左叶子节点,判断右兄弟,若为空,直接在第二个循环里返回值
//若有右兄弟,继续判断左子节点,压栈循环
stack1.push(T);
stack2.push(left);
T = T.lchild;
}
while (!stack1.isEmpty() && stack2.peek() == right) {
//如果是右子节点返回父节点,则任务完成,将两个栈顶弹出
//若没有右子节点,也一样
stack2.pop();
System.out.print(stack1.pop().value + " ");
}
if (!stack1.isEmpty() && stack2.peek() == left) {
//如果是从左子节点 返回父节点,则将标记改为右子节点
stack2.pop();
stack2.push(right);
T = stack1.peek().rchild;
}
}
}
总结:
后序遍历比较麻烦,主要原因是需要对子树的状态进行判断,再进行顺序的递归。
层序遍历:
按照树的深度从上到下,从左到右遍历
采用队列实现
先把根节点加到队列(队列的特性,加到队尾,从对头删除)
抛出对头的元素,把他的左右儿子加到队列
一层遍历完后队列中剩下的是这层的下一层的全部结点
例子:
代码实现:
public static void levelOrderNormal(TreeNode T) {
//add()和remove()方法在失败的时候会抛出异常(不推荐)
if (T == null) return;
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(T);
while (!queue.isEmpty()) {
TreeNode p = queue.poll();//从对头删除并得到元素
System.out.print(p.value + " ");
if (p.lchild != null) {
queue.offer(p.lchild);
}
if (p.rchild != null) {
queue.offer(p.rchild);
}
}
}
结果:
总结:
层序遍历用队列还是很好实现的,只要知道队列的特性就好