一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第19天,点击查看活动详情。
给定一个 n 叉树的根节点 root ,返回 其节点值的 后序遍历 。 n 叉树 在输入中按层序遍历进行序列化表示,每组子节点由空值 null 分隔(请参见示例)。
N叉树的后序遍历,对比二叉树的后序遍历很容易就能够写出来,代码如下:
public List<Integer> postorder(Node root) {
List<Integer> result = new ArrayList();
if(root == null){
return result;
}
// 遍历所有孩子结点
for(int i = 0;i < root.children.size();++i){
result.addAll(postorder(root.children.get(i)));
}
// 最后访问根结点
result.add(root.val);
return result;
}
对于非递归的写法,我们仍然使用一个栈结构来实现后序遍历的过程,后序遍历的访问顺序是先访问孩子节点,再访问根结点,所以要将孩子节点先入栈,当所有孩子均入栈后再执行遍历操作。
举个例子:
对于这样的一棵树,我们首先将根节点1入栈,然后执行一个peek操作,该操作会返回栈顶的元素,但是不会删除它,返回了根节点1后,将其孩子全部入栈,此时的栈:[3,2,4,1];再对栈进行peek操作,此时会返回栈顶元素3,再将节点3的所有孩子入栈,此时的栈:[5,6,3,2,4,1];继续peek操作,返回栈顶元素5,节点5没有孩子,那么就真正地将节点5出栈,此时的栈:[6,3,2,4,1]。
以上过程出现了一个问题,就是各个节点之间的关系已经搞不清楚了,谁是谁的孩子节点,谁是谁的父亲节点,由此,我们需要借助一个标志位来分隔一下节点之间的关系。
改进一下思路,当根节点peek操作时,向栈中压入一个null,再将根节点的所有孩子入栈:
[3,2,4,null,1]
当节点3peek操作时,也向栈中压入一个null,再将孩子入栈:
[5,6,null,3,2,4,null,1]
此时当节点5peek操作时,因为没有孩子,所以只会压入一个null:
[null,5,6,null,3,2,4,null,1]
而当null执行peek操作时,我们就清楚了到这一步已经没有孩子节点了,所以直接将null弹出,并继续弹出下一个节点5:
[6,null,3,2,4,null,1]
重复此项操作,对节点6进行peek操作时,就需要观察是否有孩子节点,没有,直接压入null:
[null,6,null,3,2,4,null,1]
继续弹栈,为null,直接弹出,并弹出下一个节点6,继续弹出null,并弹出下一个节点3,以此类推。
不难发现,此时便做到了先访问节点的孩子节点,再访问节点的遍历顺序。
代码如下:
public List<Integer> postorder(Node root) {
List<Integer> result = new ArrayList();
if(root == null){
return result;
}
Stack<Node> stack = new Stack<>();
// 根节点入栈
stack.push(root);
Node node;
while(!stack.isEmpty()){
// 得到栈顶元素
node = stack.peek();
// 若栈顶元素为空,说明子树遍历到头了
if(node == null){
// 弹出null标志
stack.pop();
// 弹出节点
node = stack.pop();
result.add(node.val);
// 重复操作
continue;
}
// 存入标志位
stack.push(null);
// 将所有孩子节点入栈
for(int i = node.children.size() - 1;i >= 0;--i){
stack.push(node.children.get(i));
}
}
return result;
}
此题得解。