浅谈:前序遍历反转法求解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) 。
相关推荐
wzdark7 小时前
基于堆的优先队列实现原理与复杂度分析4
算法
老王熬夜敲代码9 小时前
BLAKE3:最快的密码学哈希函数
算法·密码学·哈希算法
hansang_IR9 小时前
【题解】P4460 [CQOI2018] 解锁屏幕
c++·算法
见闻小天地9 小时前
简博斯JG-C系列彩色激光同轴位移计:摄像头模组对位与行程测量的技术价值观察
人工智能·算法
葫三生10 小时前
《论三生原理》神话学构想与“神话历史”理论、《古史中的神话》思路异同?
大数据·人工智能·科技·深度学习·算法
liliangcsdn10 小时前
因子分析指标概念与示例
算法·机器学习
SupL!11 小时前
LoftQ原理
算法
Navigator_Z13 小时前
LeetCode //C - 1240. Tiling a Rectangle with the Fewest Squares
c语言·算法·leetcode
稻米哟13 小时前
力扣100——双指针
算法·leetcode
明志数科14 小时前
具身智能真机采集工程实践:5类高频失效模式与规避清单
人工智能·算法·机器学习