遍历二叉树(递归,非递归都有)

本文介绍二叉树的非递归遍历方法,包括先根、中根和后根遍历,并提供详细的Go语言实现代码。通过使用栈结构来辅助遍历过程,避免了递归带来的堆栈溢出风险。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

主要描述一下非递归,递归在下面的总的代码里有:

因为非递归都用到了栈,先贴一下栈的代码

const stackSize int = 22                //栈的容量

type TreeNode struct {               //树结点
	Left *TreeNode
	Value int
	Right *TreeNode
}

type Stack struct {                     //栈结构
	Size int
	Values []*TreeNode
}

func CreateStack() Stack{                //创建栈
	s := Stack{}
	s.Size = stackSize
	return s
}

func (s *Stack) IsFull() bool {          //栈满
	return len(s.Values) == s.Size
}

func (s *Stack) IsEmpty() bool {          //栈空
	return len(s.Values) == 0
}

func (s *Stack) Push(a *TreeNode) error {       //入栈
	if s.IsFull() {
		return errors.New("Stack is full,push failed")
	}
	s.Values = append(s.Values,a)
	return nil
}

func (s *Stack) Pop() (*TreeNode,error) {//出栈,并返回其值
	if s.IsEmpty() {
		return nil ,errors.New("Out of index,len(stack) is 0")
	}
	res := s.Values[len(s.Values)-1]
	s.Values = s.Values[:len(s.Values)-1]
	return res,nil
}

func (s *Stack) Peek() (*TreeNode,error) {//查看栈顶元素
	if s.IsEmpty() {
		return nil ,errors.New("Out of index,len(stack) is 0")
	}
	return s.Values[len(s.Values)-1],nil
}

一、先根遍历的非递归算法:步骤如下:

1、创建一个栈对象,将根节点入栈;

2、当栈为非空时,将栈顶结点弹出栈并访问该结点;

3、对当前访问的非空左孩子结点相继依次访问(不需要入栈),并将访问结点的非空右孩子结点入栈

4、重复执行步骤②和步骤③,直到栈为空为止

func PreStackTraverse(t *TreeNode){//先根遍历,非递归
	if t != nil {
		S := CreateStack()
		S.Push(t)
		for !S.IsEmpty() {
			T,_ := S.Pop()
			fmt.Printf("%d ",T.Value)
			for T != nil {
				if T.Left != nil {
					fmt.Printf("%d ",T.Left.Value)
				}
				if T.Right != nil {
					S.Push(T.Right)
				}
				T = T.Left
			}
		}
	}
	fmt.Println()
}

二、中根遍历的非递归算法:步骤如下:

1、创建一个栈对象,将根节点入栈;

2、当栈为非空时,将根结点的非空左孩子相继进栈;

3、栈顶结点出栈并访问栈顶结点,并使该栈顶结点的非空右孩子结点入栈

4、重复执行步骤②和步骤③,直到栈为空为止

//中根遍历,非递归
func MidStackTraverse(t *TreeNode){
	if t != nil {
		S := CreateStack()
		S.Push(t)
		for !S.IsEmpty() {
			top,_ :=S.Peek()
			for  top != nil {       //将栈顶结点的所有左孩子结点入栈
				S.Push(top.Left)
				top,_= S.Peek()
			}
			S.Pop()                //空结点退栈
			if !S.IsEmpty() {
				T,_ := S.Pop()
				fmt.Printf("%d ",T.Value)
				S.Push(T.Right)
			}
		}
	}
	fmt.Println()
}

三、后根遍历的非递归算法:

便于理解点:

flag : 当栈中加入右孩子之后,该右孩子的左孩子也要跟着加入栈中,flag为false标记加入了一个右孩子,跳出访问栈顶的循环,将该结点的左孩子加入栈中。否则flag为true,可以继续判断该栈顶 

p :  刚刚被访问的结点,当一个结点可以被访问时,要么其右孩子为空,要么p指向其右孩子

步骤如下:

1、创建一个栈对象,根节点进栈,p赋初值null;

2、若栈非空,则栈顶结点的非空左孩子相继进栈;

3、若栈非空,查看栈顶结点,若栈顶结点的右孩子为空,或与p相等,则将栈顶结点弹出栈并访问它,同时将p指向该结点,并设置flag为true。否则将栈顶结点的右孩子(1个)压入栈中,并置flag为false

4、若flag==true,重复执行3,否则,重复执行步骤2和3,直到栈为空。

//后根遍历,非递归
func PostStackTraverse(t *TreeNode) {
	if t != nil {
		S := CreateStack()
		S.Push(t)
		var flag bool
		var p *TreeNode
		for !S.IsEmpty() {
			top,_ := S.Peek()
			for  top != nil {       //将栈顶结点的所有左孩子结点入栈
				S.Push(top.Left)
				top,_= S.Peek()
			}
			S.Pop()
			for !S.IsEmpty() {
				T,_ := S.Peek()
				if T.Right == nil || T.Right == p {
					fmt.Printf("%d ",T.Value)
					S.Pop()
					flag = true
					p = T            //p指向刚被访问的结点
				}else {
					S.Push(T.Right)
					flag = false     //有右孩子进栈了
				}
				if !flag {            //退出该循环
					break
				}
			}
		}
	}
	fmt.Println()
}

完整代码:

package main

import (
	"fmt"
	"errors"
)

const stackSize int = 22                //栈的容量

type TreeNode struct {
	Left *TreeNode
	Value int
	Right *TreeNode
}

type Stack struct {                     //栈结构
	Size int
	Values []*TreeNode
}

func CreateStack() Stack{                //创建栈
	s := Stack{}
	s.Size = stackSize
	return s
}

func (s *Stack) IsFull() bool {          //栈满
	return len(s.Values) == s.Size
}

func (s *Stack) IsEmpty() bool {          //栈空
	return len(s.Values) == 0
}

func (s *Stack) Push(a *TreeNode) error {       //入栈
	if s.IsFull() {
		return errors.New("Stack is full,push failed")
	}
	s.Values = append(s.Values,a)
	return nil
}

func (s *Stack) Pop() (*TreeNode,error) {//出栈,并返回其值
	if s.IsEmpty() {
		return nil ,errors.New("Out of index,len(stack) is 0")
	}
	res := s.Values[len(s.Values)-1]
	s.Values = s.Values[:len(s.Values)-1]
	return res,nil
}

func (s *Stack) Peek() (*TreeNode,error) {//查看栈顶元素
	if s.IsEmpty() {
		return nil ,errors.New("Out of index,len(stack) is 0")
	}
	return s.Values[len(s.Values)-1],nil
}

func (s *Stack) Traverse() {
	if s.IsEmpty() {
		fmt.Println("Stack is empty")
	}else {
		fmt.Println(s.Values)
	}
}

//建立二叉树
func TreeCreate(i int,arr []int) *TreeNode{
	t := &TreeNode{nil,arr[i],nil}
	if i<len(arr) && 2*i+1 < len(arr){
		t.Left = TreeCreate(2*i+1,arr)
	}
	if i<len(arr) && 2*i+2 < len(arr) {
		t.Right = TreeCreate(2*i+2,arr)
	}
	return t
}

//先根遍历,递归
func PreTraverse(t *TreeNode){
	if t != nil {
		fmt.Printf("%d/",t.Value)
		PreTraverse(t.Left)
		PreTraverse(t.Right)
	}
}

//先根遍历,非递归
func PreStackTraverse(t *TreeNode){
	if t != nil {
		S := CreateStack()
		S.Push(t)                               //根结点入栈
		for !S.IsEmpty() {
			T,_ := S.Pop()                      //移除根结点,并返回其值
			fmt.Printf("%d ",T.Value)    //访问结点
			for T != nil {
				if T.Left != nil {              //访问左孩子
					fmt.Printf("%d ",T.Left.Value)   //访问结点
				}
				if T.Right != nil {              //右孩子非空入栈
					S.Push(T.Right)
				}
				T = T.Left
			}
		}
	}
	fmt.Println()
}

//中根遍历,递归
func MidTraverse(t *TreeNode){
	if t != nil {
		MidTraverse(t.Left)
		fmt.Printf("%d/",t.Value)
		MidTraverse(t.Right)
	}
}

//中根遍历,非递归
func MidStackTraverse(t *TreeNode){
	if t != nil {
		S := CreateStack()
		S.Push(t)
		for !S.IsEmpty() {
			top,_ :=S.Peek()
			for  top != nil {       //将栈顶结点的所有左孩子结点入栈
				S.Push(top.Left)
				top,_= S.Peek()
			}
			S.Pop()                //空结点退栈
			if !S.IsEmpty() {
				T,_ := S.Pop()
				fmt.Printf("%d ",T.Value)
				S.Push(T.Right)
			}
		}
	}
	fmt.Println()
}

//后根遍历,递归
func PostTraverse(t *TreeNode){
	if t != nil {
		PostTraverse(t.Left)
		PostTraverse(t.Right)
		fmt.Printf("%d/",t.Value)
	}
}

//后根遍历,非递归
func PostStackTraverse(t *TreeNode) {
	if t != nil {
		S := CreateStack()
		S.Push(t)
		var flag bool
		var p *TreeNode
		for !S.IsEmpty() {
			top,_ := S.Peek()
			for  top != nil {       //将栈顶结点的所有左孩子结点入栈
				S.Push(top.Left)
				top,_= S.Peek()
			}
			S.Pop()
			for !S.IsEmpty() {
				T,_ := S.Peek()
				if T.Right == nil || T.Right == p {
					fmt.Printf("%d ",T.Value)
					S.Pop()
					flag = true
					p = T            //p指向刚被访问的结点
				}else {
					S.Push(T.Right)
					flag = false     //有右孩子进栈了
				}
				if !flag {            //退出该循环
					break
				}
			}
		}
	}
	fmt.Println()
}


func main(){
	arr := []int{3,9,6,8,7,11,1,22,21}
	Tree := TreeCreate(0,arr)

    fmt.Print("前根,递归:")
	PreTraverse(Tree)
	fmt.Print("   前根,非递归:")
	PreStackTraverse(Tree)

	fmt.Print("中根,递归:")
	MidTraverse(Tree)
	fmt.Print("   中根,非递归:")
	MidStackTraverse(Tree)

	fmt.Print("后根,递归:")
	PostTraverse(Tree)
	fmt.Print("   后根,非递归:")
	PostStackTraverse(Tree)
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值