1. 程式人生 > >遍歷二叉樹(遞迴,非遞迴都有)

遍歷二叉樹(遞迴,非遞迴都有)

主要描述一下非遞迴,遞迴在下面的總的程式碼裡有:

因為非遞迴都用到了棧,先貼一下棧的程式碼

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)
}