数据结构 - 使用栈遍历二叉树
练手使用栈遍历二叉树
介绍
常见的二叉树遍历方式为递归实现先序、中序、后序遍历操作,比较容易理解并且编码,递归其实也是依赖系统提供的方法栈进行的操作。其实我们也可以自己维护一个栈,对二叉树进行遍历操作,且可以很方便得实现遍历流程的终止行为。
代码
type Node struct {
Left *Node
Right *Node
Value int
}
type Tree struct {
Root *Node
}
// 使用栈模拟先根遍历
func (t *Tree) PreOrderTravel(cb func (*Node)) {
stack := make([]*Node, 0)
n := t.Root
for len(stack) != 0 || n != nil {
// 循环遍历左子树,将节点入栈
for n != nil {
stack = append(stack, n)
// 此时可对加入的元素进行操作,即为先根操作
cb(n)
n = n.Left
}
// 此时栈顶的节点是没有左子树的
// 取出并将指针指向右孩子
top := stack[len(stack)-1]
stack = stack[:len(stack)-1]
n = top.Right
}
}
// 使用栈模拟中根遍历
func (t *Tree) InOrderTravel(cb func (*Node)) {
stack := make([]*Node, 0)
n := t.Root
for len(stack) != 0 || n != nil {
// 循环遍历左子树,将节点入栈
for n != nil {
stack = append(stack, n)
n = n.Left
}
// 此时栈顶的节点是没有左子树的
// 取出后可做操作,此时为中根操作
// 例如:对于叶子节点A来说,A是其父节点(根)的左孩子,因此在此步会先于父节点被取出操作,符合 左、根、右
top := stack[len(stack)-1]
stack = stack[:len(stack)-1]
cb(top)
// 操作结束后将指针指向右孩子
n = top.Right
}
}
// 使用栈模拟后根遍历
func (t *Tree) PostOrderTravel(cb func (*Node)) {
stack := make([]*Node, 0)
n := t.Root
// 用于标记上一次遍历节点的指针
var prev *Node
for len(stack) != 0 || n != nil {
// 循环遍历左子树,将节点入栈
for n != nil {
stack = append(stack, n)
n = n.Left
}
// 查看栈顶节点,若其没有右孩子,或者右孩子已经遍历过了(prev指针标记),则从栈顶弹出
// 弹出时可以对弹出的节点做后根操作
top := stack[len(stack)-1]
if top.Right == nil || top.Right == prev {
stack = stack[:len(stack)-1]
cb(top)
// 标记当前节点的左右子树均已遍历过
prev = top
} else {
// 存在右孩子且右孩子还未被遍历,则往右子树遍历
n = top.Right
}
}
}
func main() {
// 定义一个先根遍历时顺序为1,2,3,4,5,6,7的二叉树
// 1
// 2 5
// 3 4 6 7
root := &Node{Value: 1, Left: &Node{Value: 2, Left: &Node{Value: 3}, Right: &Node{Value: 4}}, Right: &Node{Value: 5, Left: &Node{Value: 6}, Right: &Node{Value: 7}}}
tree := &Tree{Root: root}
// 定义遍历时的操作函数
var print = func(n *Node) {
fmt.Printf("%d ", n.Value)
}
// 先根遍历
tree.PreOrderTravel(print) // 输出 1 2 3 4 5 6 7
fmt.Println()
// 中根遍历
tree.InOrderTravel(print) // 输出 3 2 4 1 6 5 7
fmt.Println()
// 后根遍历
tree.PostOrderTravel(print) // 输出 3 4 2 6 7 5 1
fmt.Println()
}
最后修改于 2022-02-12