本文已参与「新人创作礼」活动, 一起开启掘金创作之路。
给定一个二叉树,返回它的 后序 遍历。
示例:
输入: [1,null,2,3]
1
\ 2
/
3输出: [3,2,1]
进阶: 递归算法很简单,你可以通过迭代算法完成吗?来源:力扣(LeetCode)
思路:
递归法:就是根据 左右根 的顺序递归即可
迭代法:需要借助栈先进后出的特性。
有一个标记根节点的flagMap,用来标记第几次经过某个根节点root,只有第二次经过某个根节点时,才存储其结果,并将其出栈置空。
- 1满足 root != nil 的条件,1入栈,1的flag标记为1,第一次经过;root = root.Left 为空,退出内循环;
- 栈顶元素为1,故满足 len(stack) > 0 与 flagMap[root] == 1 的条件:节点1的flag被标记为2,遍历1的右子节点2,第二次进入内循环 root != nil,2入栈且标记为1。
- 紧接着3入栈且标记为1,3的左子节点为空,不满足 for root != nil { 条件,跳出内循环;此时栈中元素自顶向下为 3 -> 2 -> 1。
- 栈顶元素为3,且满足 len(stack) > 0 与 flagMap[root] == 1 的条件,节点3的flag被标记为2,此时3的右子节点为当前root(空)。开始新一轮循环,再次获取栈顶节点3,因不满足内循环, len(stack) > 0 且3的右子节点(当前root)的flag为2,故进入else判断分支,结果数组更新为 res={3} ,3出栈且置空。
- 此时栈顶节点为2,且满足 len(stack) > 0与 flagMap[root] == 1 的条件,节点2的flag被标记为2,root = root.Right 节点2的右子节点为空。进行新一轮外层循环。因不满足内层循环故跳过内循环。最后来到else分支,结果数组更新为 res={3,2} ,2出栈且置空。
- 同理可得,结果数组更新为 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
}