优雅的进行二叉树的后序遍历
树的遍历是一个常用的操作,分为递归和非递归遍历,经典的递归遍历如下
List<Integer> lists=new ArrayList<>();
/**
* 后序递归
* @param node
* @return
*/
public List<Integer> backRecuErgodic(TreeNode node){
if(node!=null){
backRecuErgodic(node.left);
backRecuErgodic(node.right);
lists.add(node.val);
}
return lists;
}
这种方式非常简洁,而且前序,中序,后序遍历的格式几乎完全统一,很好记忆
不过在非递归的情况下,大家的解法就复杂了起来,对于前序和中序遍历的非递归实现,代码风格是一致的,而对于后序遍历,大家的解法就复杂多样了
/**
* 先序非递归
* @param node
* @return
*/
public List<Integer> preNoRecuErgodic(TreeNode node){
Stack<TreeNode> stack=new Stack<>();
while (node!=null||stack.isEmpty()==false){
if(node!=null){
lists.add(node.val);
stack.push(node);
node=node.left;
}else {
node=stack.pop();
node=node.right;
}
}
return lists;
}
/**
* 中序非递归
* @param node
* @return
*/
public List<Integer> inNoRecuErgodic(TreeNode node){
Stack<TreeNode> stack=new Stack<>();
while (stack.isEmpty()==false||node!=null){
if(node!=null){
stack.push(node);
node=node.left;
}else {
node=stack.pop();
lists.add(node.val);
node=node.right;
}
}
return lists;
}
其实后序遍历也可以有这样统一的格式,试一下
1
2 3
4 5 6 7
仔细观察一下上面的树,其后序遍历的结果为
4 5 2 6 7 3 1
前序遍历的结果为
1 2 4 5 3 6 7
若我们换个思路,按照前序遍历的思想,只不过我们先遍历右儿子,那遍历结果为
1 3 7 6 2 5 4
怎么样,是不是和后序遍历结果很像?按照先序遍历的规则,把先遍历左儿子转为先遍历右儿子,我们就可以得到一个神奇的结论,这种规则情况下的遍历的操作,得到的结果是后序遍历的相反的顺序。
其实后序遍历和前序遍历有着一种镜像关系,后序遍历可以有原来树的镜像树(镜像树可以看leetcode上的一道题)的前序遍历的逆序得到
于是,我们可以得到一个与前序和后序遍历同样形式的代码
List<Integer> lists=new ArrayList<>();
public List<Integer> backNoRecuErgodic(TreeNode node){
Stack<TreeNode> stack=new Stack<>();
while (stack.isEmpty()==false||node!=null){
if(node!=null){
stack.push(node);
lists.add(0,node.val);//头插正好逆序。后序遍历和前序遍历是镜像问题
node=node.right;
}else {
node=stack.pop();
node=node.left;
}
}
return lists;
}
不过需要注意的是,ArrayList底层是使用数组实现的,头插的效率较低,需要经过数组拷贝,因此插入头部的复杂度为O(N)
LinkedList是双向链表,插入头部的时间复杂度是O(1)于是我们可以用LinkedList来代替ArrayList,这样效率更高
List<Integer> lists=new LinkedList<>();
public List<Integer> backNoRecuErgodic(TreeNode node){
Stack<TreeNode> stack=new Stack<>();
while (stack.isEmpty()==false||node!=null){
if(node!=null){
stack.push(node);
lists.offerFirst(node.val);//头插正好得到逆序。
node=node.right;//这里先去遍历的右儿子
}else {
node=stack.pop();
node=node.left;
}
}
return lists;
}