二叉树的深度优先遍历。有三种顺序:
- 中序(inorder):root、左、右
- 或 root、右、左,没有本质区别
- 前序(preorder):左,root,右
- 后序(postorder):左,右,root
这里先阐述算法思路,后面给出完整代码示例。算法用递归写很容易,这里讲述 不用递归、用栈实现 的方法。还有一些奇技淫巧,连栈都不需要,我没有深入了解。
中序
算法:
- Create an empty stack S.
- Initialize current node as root
- Push the current node to S and set current = current->left until current is NULL
- If current is NULL and stack is not empty then
- Pop the top item from stack.
- Print the popped item, set current = popped_item->right
- Go to step 3.
- If current is NULL and stack is empty then we are done.
重点在于如何定义一次循环的逻辑边界。在这里是这样定义的:
- 每次循环,给定一个头节点,将其一系列的左节点入栈
- 如果这个头节点为空,跳过此步骤
- 从栈 Pop 出一个节点,把其添加到遍历结果中
- 把该节点的右节点列为下次循环的头节点
重点在于加粗部分的逻辑,把连续 pop 给表达了进来。每次都把某一批左左左节点入栈。
前序
有两种做法:
- 可以跟中序类似,区别仅在 push 元素进结果列表(answer)的时机不同;见下面代码中的
preorderTraversal()
- 更简洁的做法,从 root 入栈开始,每个循环 pop 出一个元素,并先后将其右节点、左节点入栈;见下面代码中的
preorderTraversal2()
后序
后序是最难的部分。因为后序的遍历过程不是个尾递归问题(未理解)。这篇 文章 描述 用两个栈 的思路,非常好理解。它的思路是,把遍历过程反过来,把后面要访问的节点先入栈,最终再从栈里出一个个 pop 出来。
看看代码实现就能理解。
代码
下面的代码以这颗树为测试用例:
1
/ \
2 3
/ \ \
4 5 6
/ / \
7 8 9
\
10
package main
import "fmt"
type TreeNode struct {
Val int
Left *TreeNode
Right *TreeNode
}
func inorderTraversal(root *TreeNode) []int {
answer := []int{}
stack := []*TreeNode{}
curr := root
for curr != nil || len(stack) != 0 {
for curr != nil {
stack = append(stack, curr)
curr = curr.Left
}
curr, stack = stack[len(stack)-1], stack[:len(stack)-1]
answer = append(answer, curr.Val)
curr = curr.Right
}
return answer
}
// Solution 1: Inorder 版稍加修改
func preorderTraversal(root *TreeNode) []int {
answer := []int{}
stack := []*TreeNode{}
curr := root
for curr != nil || len(stack) != 0 {
for curr != nil {
answer = append(answer, curr.Val)
stack = append(stack, curr)
curr = curr.Left
}
curr, stack = stack[len(stack)-1], stack[:len(stack)-1]
curr = curr.Right
}
return answer
}
// Solution 2: 更好理解的方式
func preorderTraversal2(root *TreeNode) []int {
answer := []int{}
if root == nil {
return answer
}
stack := []*TreeNode{root}
for len(stack) != 0 {
curr := stack[len(stack)-1]
stack = stack[:len(stack)-1]
answer = append(answer, curr.Val)
if curr.Right != nil {
stack = append(stack, curr.Right)
}
if curr.Left != nil {
stack = append(stack, curr.Left)
}
}
return answer
}
func postorderTraversal(root *TreeNode) []int {
answer := []int{}
firstStack := []*TreeNode{root}
secondStack := []*TreeNode{}
if root == nil {
return answer
}
for len(firstStack) != 0 {
curr := firstStack[len(firstStack)-1]
firstStack = firstStack[:len(firstStack)-1]
secondStack = append(secondStack, curr)
if curr.Left != nil {
firstStack = append(firstStack, curr.Left)
}
if curr.Right != nil {
firstStack = append(firstStack, curr.Right)
}
}
for len(secondStack) != 0 {
answer = append(answer, secondStack[len(secondStack)-1].Val)
secondStack = secondStack[:len(secondStack)-1]
}
return answer
}
func main() {
root := &TreeNode{1,
&TreeNode{2,
&TreeNode{4, &TreeNode{7, nil, nil}, nil},
&TreeNode{5, nil, nil}},
&TreeNode{3, nil,
&TreeNode{6,
&TreeNode{8, nil, nil},
&TreeNode{9,
nil,
&TreeNode{10, nil, nil},
},
}}}
fmt.Println(inorderTraversal(root))
fmt.Println(preorderTraversal(root))
fmt.Println(preorderTraversal2(root))
fmt.Println(postorderTraversal(root))
}