思路:
如你所知,在前序遍历中,我们会先遍历节点本身,然后从左向右依次先序遍历该每个以子节点为根的子树,而在后序遍历中,需要先从左到右依次遍历每个以子节点为根的子树,然后再访问根节点。例如:当前的节点为 u ,它的从左至右子节点依次为 v1, v2, v3,此时我们可以知道它的前序遍历结果为:
{u,v1,children(v1),v2,children(v2),v3,children(v3)}
后序遍历结果为:
{children(v1),v1,children(v2),v2,children(v3),v3,u}
其中 children(vk) 表示以 vk 为根节点的子树的遍历结果(不包括 vk )。仔细观察可以知道,将前序遍历中子树的访问顺序改为从右向左可以得到如下访问顺序:
{u,v3,children(v3),v2,children(v2),v1,children(v1)}
将上述的结果进行反转,得到:
{children(v1),v1,children(v2),v2,children(v3),v3,u}
刚好与后续遍历的结果相同。此时我们可以利用前序遍历,只不过前序遍历中对子节点的遍历顺序是从左向右,而这里是从右向左。因此我们可以使用和 N 叉树的前序遍历相同的方法,使用一个栈来得到后序遍历。我们首先把根节点入栈。当每次我们从栈顶取出一个节点 u 时,就把 u 的所有子节点顺序推入栈中。例如 u 的子节点从左到右为 v1, v2, v3,那么推入栈的顺序应当为 v1, v2, v3, 这样就保证了出栈顺序是从右向左,下一个遍历到的节点(即 u 的右侧第一个子节点 v3)出现在栈顶的位置。在遍历结束之后,我们把遍历结果进行反转,就可以得到后序遍历。

代码
Java
class Solution {
public List<Integer> postorder(Node root) {
List<Integer> res = new ArrayList<>();
if (root == null) {
return res;
}
Deque<Node> stack = new ArrayDeque<Node>();
stack.push(root);
while (!stack.isEmpty()) {
Node node = stack.pop();
res.add(node.val);
for (Node item : node.children) {
stack.push(item);
}
}
Collections.reverse(res);
return res;
}
}
C++
class Solution {
public:
vector<int> postorder(Node* root) {
vector<int> res;
if (root == nullptr) {
return res;
}
stack<Node *> st;
st.emplace(root);
while (!st.empty()) {
Node * node = st.top();
st.pop();
res.emplace_back(node->val);
for (auto &item : node->children) {
st.emplace(item);
}
}
reverse(res.begin(), res.end());
return res;
}
};
C#
public class Solution {
public IList<int> Postorder(Node root) {
IList<int> res = new List<int>();
if (root == null) {
return res;
}
Stack<Node> stack = new Stack<Node>();
stack.Push(root);
while (stack.Count > 0) {
Node node = stack.Pop();
res.Add(node.val);
foreach (Node item in node.children) {
stack.Push(item);
}
}
((List<int>) res).Reverse();
return res;
}
}
C
#define MAX_NODE_SIZE 10000
int* postorder(struct Node* root, int* returnSize) {
if (NULL == root) {
*returnSize = 0;
return NULL;
}
int * res = (int *)malloc(sizeof(int) * MAX_NODE_SIZE);
struct Node ** stack = (struct Node **)malloc(sizeof(struct Node *) * MAX_NODE_SIZE);
int pos = 0, top = 0;
stack[top++] = root;
while (top != 0) {
struct Node * node = stack[--top];
res[pos++] = node->val;
for (int i = 0; i < node->numChildren; i++) {
stack[top++] = node->children[i];
}
}
free(stack);
for (int l = 0, r = pos - 1; l < r; l++, r--) {
int tmp = res[l];
res[l] = res[r];
res[r] = tmp;
}
*returnSize = pos;
return res;
}
复杂度分析
- 时间复杂度:时间复杂度:O(m) ,其中 m 为 N 叉树的节点。每个节点恰好被访问一次。
- 空间复杂度:O(m) ,其中 m 为 N 叉树的节点。如果 N 叉树的深度为 1 则此时栈的空间为 O(1) ,如果 N 叉树的深度为 1 则此时栈的空间为 O(m−1) ,平均情况下栈的空间为 O(logm) ,因此空间复杂度为 O(m) 。