LeetCode:145. 二叉树的后序遍历

181 阅读2分钟

本文已参与「新人创作礼」活动, 一起开启掘金创作之路。

145. 二叉树的后序遍历

给定一个二叉树,返回它的 后序 遍历。

示例:

输入: [1,null,2,3]  
1
\      2
/

输出: [3,2,1]
进阶: 递归算法很简单,你可以通过迭代算法完成吗?

来源:力扣(LeetCode)

思路:
递归法:就是根据 左右根 的顺序递归即可
迭代法:需要借助先进后出的特性。
有一个标记根节点的flagMap,用来标记第几次经过某个根节点root,只有第二次经过某个根节点时,才存储其结果,并将其出栈置空。

  1. 1满足 root != nil 的条件,1入栈,1的flag标记为1,第一次经过;root = root.Left 为空,退出内循环;
  2. 栈顶元素为1,故满足 len(stack) > 0 与 flagMap[root] == 1 的条件:节点1的flag被标记为2,遍历1的右子节点2,第二次进入内循环 root != nil,2入栈且标记为1
  3. 紧接着3入栈且标记为1,3的左子节点为空,不满足 for root != nil { 条件,跳出内循环;此时栈中元素自顶向下为 3 -> 2 -> 1。
  4. 栈顶元素为3,且满足 len(stack) > 0 与 flagMap[root] == 1 的条件,节点3的flag被标记为2,此时3的右子节点为当前root(空)。开始新一轮循环,再次获取栈顶节点3,因不满足内循环, len(stack) > 0 且3的右子节点(当前root)的flag为2,故进入else判断分支,结果数组更新为 res={3} ,3出栈且置空。
  5. 此时栈顶节点为2,且满足 len(stack) > 0与 flagMap[root] == 1 的条件,节点2的flag被标记为2,root = root.Right 节点2的右子节点为空。进行新一轮外层循环。因不满足内层循环故跳过内循环。最后来到else分支,结果数组更新为 res={3,2} ,2出栈且置空。
  6. 同理可得,结果数组更新为 res={3,2,1} ,1出栈且置空。

​编辑

时间复杂度:
O(n),我们需要遍历树的每一个节点,树一共有 n 个节点,所以时间复杂度为 n。

空间复杂度:
O(n),因为是迭代遍历,需要一个stack空间存储中间节点,还需要一个result空间存储结果,所以空间复杂度为 2n,也就是 n。

// 递归:
func postorderTraversal(root *TreeNode) []int {
  	res := make([]int, 0)

	var recursion func(root *TreeNode)
	recursion = func(root *TreeNode) {
		if root != nil {
			recursion(root.Left)
			recursion(root.Right)
         res = append(res, root.Val)
		}
	}
	recursion(root)

	return res
}

// 迭代:
// https://github.com/HelloWorld-666/C_Tree/blob/master/C_Tree/main.cpp (之前用C++同思路实现的)
// 后序:左 右 根 (每个节点会经过两次,第一次不断遍历左子节点会经过,第二次遍历完右子节点后,获取栈顶节点时也会经过;且当某个根节点root左右孩子节点都为空时,root出栈并置空)
func postorderTraversal(root *TreeNode) []int {
   	res := make([]int, 0)
	stack:= make([]*TreeNode, 0)
	flagMap := make(map[*TreeNode]int)          // 标记节点是第几次经过根节点 => 入栈

	for root != nil || len(stack) > 0 {
		for root != nil {                       // 遍历左子节点
			stack = append(stack, root)         // push
            flagMap[root] = 1                   // 第1次经过该节点时,做标记:1
			root = root.Left
		}

		if len(stack) > 0 {
            root = stack[len(stack) - 1]        // 获取栈顶节点
			if flagMap[root] == 1 {
                flagMap[root] = 2               // 第2次经过该节点时,做标记:2
				root = root.Right
			} else {
				res = append(res, root.Val)
				stack = stack[:len(stack) - 1]  // pop stack top node
                root = nil // 当前root的左右子节点都为空时(叶子节点),将该root出栈且置空,避免该root因不等于空而再次进入上方内循环中逻辑.
			}
		}
	}
	return res
}