二叉树的后序遍历算法比较难,尤其是非递归。但是后序遍历的应用最广泛,最容易出现算法题,因此一定要牢牢掌握。其中迭代的方法又较递归的方法快,但是也是非常令人头疼的一件事。
1.递归算法
public void postOrderRecur(TreeNode root){
if(root != null){
postOrderRecur(root.left);
postOrderRecur(root.right);
visit(root);
}
}
递归算法比较简单,一般没有特殊要求的时候,首选递归算法。
2.非递归算法
我们知道,递归算法其实就是调用了系统栈,因此我们可以通过自己使用一个辅助栈将递归算法改为迭代的。
2.1 栈+集合
使用栈将递归转非递归,使用集合在常数时间内可以判断一个结点的孩子是否被访问过:
public void postOrder_(){
if(root == null) return;
Stack<TreeNode> s = new Stack<>();
Set<TreeNode> visit = new HashSet<>();
TreeNode p = root;
s.push(p);
while(!s.isEmpty()){
p = s.peek();
if(p.left != null && !visit.contains(p.left)){//左孩子存在且尚未被访问过
s.push(p.left);
}else if(p.right != null && !visit.contains(p.right)){//右孩子存在且未被访问过
s.push(p.right);
}else{
p = s.pop();
visited(p);//访问节点
visit.add(p);
}
}
return;
}
2.2 栈+prior
prior用于记录上一个被访问过的结点。如果一个结点的右孩子为空,或者右孩子已经被访问过了,则该节点可以被访问。判断右孩子为空不难,但是怎么判断右孩子已经被访问过了呢,当然我们可以像2.1中那样,额外使用一个集合,但是这里我们利用一条性质(书上没讲,别的地方也不会讲,其实也不算性质,就是在做题的时候发现的一条规律):在后序遍历序列中,一个结点的右孩子(如果存在)一定是紧挨着该节点并在该节点前面的。例如上图中的8前面是它的右孩子10,2前面是5,3前面是7…
那么我们想想,是不是当我们的上一个访问结点prior的值是某个结点p的右孩子的时候,那么这次一定轮到这个p结点被访问;
又因为我们知道,根据后序便利的规则,对于一个非空节点,每次我们都是先直接找到它的最左下子孙结点开始第一次访问的。例如图中的1,我们一直找到最左下结点8,才开始第一次访问,路径上的结点都暂存在栈中,然后回退的时候,才从栈中取出他们进行访问。也就是说,当从栈中取出一个结点的时候,它的左孩子已经必然被访问过了,因此我们这个时候只需要考虑它的右孩子。
而它的右孩子只有三种情况:
- 右孩子不存在,那么我们就可以直接访问该节点了;
- 右孩子存在但没被访问过,怎么判断呢,就是上面说的,右孩子不等于prior,那么我们开始转到右孩子那里去处理;
- 右孩子已经被访问过,前面说了,如果右孩子被访问了,那么接下来要被访问的必然是它自己,那么必定有右孩子==prior成立,此时我们可以访问该节点了。
public void postOrder(){
if(root == null) return;
Stack<TreeNode> s = new Stack<>();
TreeNode p = root, prior = null;
while (p != null || !s.isEmpty()){//p不为空或者栈不空
while (p != null){//找到一个子树的最左下节点
s.push(p.left);
p = p.left;
}
p = s.peek();//从栈中取出的这个结点,其左孩子必定已经被访问过
if (p.right != null && p.right != prior) {//如果该节点的右子树不空,且没有被访问过,转向右子树
p = p.right;
s.push(p);
p = p.left;
} else {//访问该节点
p = s.pop();
visit(p);
prior = p;//标识访问过的节点,对于一个节点来说,他在后序遍历中的顺序是紧跟着其右孩子的(右孩子不空时)
p = null;//访问完,将其指向空,因为对于栈顶节点(该节点的父亲),其左孩子已经被访问过了。
}
}
}
3.二叉树后序遍历的应用
二叉树的后序遍历有一个性值:每当访问一个节点时,栈中的结点正好全是它的祖先结点。我们可以利用这个性质求树的深度,求从根节点到某节点的路径,求两个结点的公共祖先等等,力扣上有很多这样的题目。