浅谈:前序遍历反转法求解N叉树的后序遍历

思路:

如你所知,在前序遍历中,我们会先遍历节点本身,然后从左向右依次先序遍历该每个以子节点为根的子树,而在后序遍历中,需要先从左到右依次遍历每个以子节点为根的子树,然后再访问根节点。例如:当前的节点为 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) 。
相关推荐
北风toto2 小时前
中缀、前缀、后缀表达式
算法·软件设计师
听取WA声一片(无恶意)2 小时前
CSP-J/CSP-S 深度优先搜索(DFS)完全讲义
c++·算法·深度优先
码流怪侠3 小时前
2026年8月GitHub热榜深度拆解:Agent Skills席卷开源圈,一个“技能包“收割5万星
算法·程序员·github
hold?fish:palm3 小时前
30 两两交换链表中的节点
数据结构·算法·链表
Nil2083 小时前
leetcode 98验证二叉搜索树
算法·leetcode·职场和发展
不正经学生3 小时前
C语言结构体:自定义数据类型,让变量打包出行
java·c语言·开发语言·数据结构·算法
cxr8284 小时前
数学的本质:关系、模式与不变量的结构世界
人工智能·算法·机器学习
初夏睡觉5 小时前
原创题目 - 空壳恋爱
c++·算法·dp·oi·信息学·原创题目·值得挑战的题目
Buke..5 小时前
【APP 逆向】哔哩哔哩 sign 参数逆向(下):OLLVM 混淆还原 sign 算法
java·javascript·爬虫·python·算法