二叉树的递归遍历很简单清晰,但是迭代遍历的话是有一定难度的,尤其是迭代中序遍历,因此在这里对二叉树三种前中后序遍历的迭代实现做一个小总结。
先序迭代
先序迭代利用额外的一个栈来辅助实现,类似于BFS,但与BFS不同的是,每次压栈需要先压入右子节点,再压左子节点,下面以Golang描述。
func preorderTraversal(root *TreeNode) (res []int) {
if root == nil{
return
}
stack := []*TreeNode{root}
for len(stack) != 0{
cur := stack[len(stack)-1]
stack = stack[0:len(stack)-1]
res = append(res, cur.Val)
if cur.Right != nil{
stack = append(stack, cur.Right)
}
if cur.Left != nil{
stack = append(stack, cur.Left)
}
}
return
}
后序遍历
之所以不先写中序,是因为后序迭代的代码其实可以很容易地由先序改来,我们知道先序是根左右的遍历顺序,其中“左”、“右”是根据压入栈的顺序来调整的,因此我们可以通过调整压栈顺序来得到 根右左的遍历次序,得到了根右左,那么后序遍历的左右根就可以由根右左逆序得到,下面以Golang来描述过程:
func postorderTraversal(root *TreeNode) (res []int) {
if root == nil{
return
}
stack := []*TreeNode{root}
for len(stack)!=0{
cur := stack[len(stack)-1]
stack = stack[0:len(stack)-1]
res = append(res, cur.Val)
if cur.Left != nil{
stack = append(stack, cur.Left)
}
if cur.Right != nil{
stack = append(stack, cur.Right)
}
}
for i,j:=0,len(res)-1; i<j;{
temp := res[i]
res[i] = res[j]
res[j] = temp
i ++
j --
}
return
}
中序遍历
中序遍历相对来说较难一些,但也是利用一个辅助栈来完成,基本思想是首先将所有左子节点沿着根向下遍历入栈直到没有左子节点,之后开始向外弹栈,弹栈的过程中再考虑元素的右子树;
具体实现如下:
func inorderTraversal(root *TreeNode) []int {
res, stack := []int{}, []*TreeNode{}
if root != nil{
for root != nil || len(stack) != 0{
if root != nil{ //当前节点压栈,再继续检查左子树
stack = append(stack, root)
root = root.Left
}else{ // 左子树走到了尽头,将当前节点回退到父节点,将父节点写入结果集,同时栈弹出,再继续检查右子树
root = stack[len(stack)-1]
res = append(res, stack[len(stack)-1].Val)
stack = stack[0:len(stack)-1]
root = root.Right
}
}
}
return res
}