二叉树顾名思义就是最多有二个叉,即最多两个子节点,且不要求节点之间的大小关系。
二叉树的遍历方式按照“左右根”的顺序不同分为以下几种:
- 前序遍历:根结点 ---> 左子树 ---> 右子树
- 中序遍历:左子树---> 根结点 ---> 右子树
- 后序遍历:左子树 ---> 右子树 ---> 根结点
- 层次遍历:只需按层次遍历即可
另外,根据遍历的策略,即“是不是一条道走到黑”而分为深度遍历(DFS)和广度遍历(BFS)。对每一个可能的分支路径深入到不能再深入为止,而且每个结点只能访问一次,那就是深度遍历。而深度遍历适用于前序、中序和后序;广度遍历适用于层次遍历。
- 前序遍历:根结点 ---> 左子树 ---> 右子树 DFS
- 中序遍历:左子树---> 根结点 ---> 右子树 DFS
- 后序遍历:左子树 ---> 右子树 ---> 根结点 DFS
- 层次遍历:只需按层次遍历即可 BFS
可以看出这个序主要是针对根节点来说的,先遍历根节点就是先序遍历(前序遍历),后遍历根节点就是后序遍历,中间到根节点就是中序遍历。二叉树遍历可以使用递归也可以使用迭代,递归的这里不讨论(递归损耗资源),只讨论迭代。而迭代一般使用队列或者堆栈实现。
一般倒序用堆栈, 正序用队列。
DFS里面最复杂的是后续遍历,这里用堆栈。直接看代码:
func postorderTraversal(root *TreeNode) (res []int) {
stk := []*TreeNode{}
var prev *TreeNode
for root != nil || len(stk) > 0 {
for root != nil {
stk = append(stk, root)
root = root.Left
}
root = stk[len(stk)-1]
stk = stk[:len(stk)-1]
// 该节点是叶子节点 或者 该节点的右孩子已遍历过
if root.Right == nil || root.Right == prev {
res = append(res, root.Val)
prev = root
root = nil
} else {
// 该节点还有右孩子因此要把它入队,暂不能遍历
stk = append(stk, root)
root = root.Right
}
}
return
}
其他的先序遍历、中序遍历的原理都和这个差不多。可以思考一下怎么写。
层次遍历和上面的DFS不一样,输出和我们看各节点的顺序一致,应该用队列,其实是BFS的思路。代码如下:
func levelOrder(root *TreeNode) [][]int {
var ans [][]int
queue := []*TreeNode{root}
for len(queue) > 0 {
level_size := len(queue)
var level_element []int
for i := 0; i < level_size; i++ {
node := queue[0]
queue = queue[1:]
if node != nil {
level_element = append(level_element, node.Val)
} else {
continue
}
if i == level_size -1 {
// the last element
ans = append(ans, level_element)
}
if node.Left != nil {
queue = append(queue, node.Left)
}
if node.Right != nil {
queue = append(queue, node.Right)
}
}
}
return ans
}
最后,再来个拓展,请写出一棵二叉树的右视图。提示:可以使用层次遍历,每层的最后一个元素就是最右边的一个元素。
参考: