文章目錄
一、結構體
1.1 如何定義結構體
type name struct{
filed1 type
filed2 type
...
}
1.2 如何創建結構體
1.2.1 使用 new
t := new(T),其中 t 爲結構體指針變量
1.2.2 使用字面量
t := T{filed1:value,filed2:value,...}
1.2.3 聲明
var t T
1.3 訪問結構體中的成員
無論變量是結構體類型還是結構體類型指針,
都可以使用 "." 給結構體中字段賦值,"." 也可以獲取結構體字段的值,
即 "結構體.字段名"
例如:
structObject.Name = "rose"
structObject.Age = 15
1.4 自定義包的結構體
結構體類型再定義它的包中必須是唯一的,它的完全類型命名是:
pack.struct 形式
1.5 結構體作爲參數傳輸
1.5.1 形式參數傳輸
傳遞副本,操作副本,不影響原對象
1.5.2 指針參數傳輸
傳遞指針,操作原對象,影響源對象狀態
1.6 帶標籤的結構體
type name struct{
field1 type "tag content"
field2 type "tag content"
...
}
附屬於字段的標籤,用於文檔說明或者開發過程中的特殊標記
標籤的值是一串字符串,標籤的內容只有通過 refect(反射) 包獲取
values := refect.ValueOf(interface{}) 獲取變量值
types := refect.TypeOf(interface{}) 獲取變量類型
counts := value.NumField() //獲取結構體所有字段數量(私有+公有+匿名 總和)
tag := type.Field(index).Tag //獲取字段對應的附屬標籤
1.7 匿名字段
字段沒有顯示名字,僅有類型,此時類型就是字段名
同樣遵循: 小寫 private,大寫 public(私有/公有是針對包而言)
在一個結構體中,對於每種數據類型只能有一個匿名字段
1.8 內嵌結構體
GO 語言沒有繼承和多態,只有封裝
繼承是通過內嵌或者組合來實現
type admin struct{
user Man.Person
level string
}
1.9 結構體存放在數組或切片中
1.10 值語義和引用語義
二、方法
2.1 方法聲明和調用
2.1.1 聲明
func (receiver type) methodName(arg1 type,...)(output1 type,...){
// TO DO
}
方法名大寫開頭 public,小寫開頭 private (私有/公有是針對包而言)
2.1.2 調用
receiver.methodName(arg1 type,...)
如果 receiver 是一個指針,實例是值類型,receiver.methodName 和(&receiver).methodName 是等價的
如果 receiver 是一個值,實例是引用傳遞,receiver.methodName 和 (*receiver).methodName 也是等價的
2.1.3 值接收者 VS 指針接收者
要改變內容必須只用指針接收者
結構體過大也考慮使用指針接收者
一致性: 如有指針接收者,最好都是指針接收者
2.2 方法和函數的區別
是否有接收者
2.3 如何擴展任意類型的方法
2.3.1 定義別名
例如: 模擬 一個棧結構、隊列結構
2.3.2 使用組合
type admin struct{
user Man.Person
level string
}
2.4 工廠模式
type File struct{
fb int
name string
}
//工廠方法
func NewFile(fb int,name string) *File{
if fb < 0 {
return nil
}
return &File{fb,name}
}
2.5 未導出字段
私有字段(字段小寫開頭,不能用點語法直接訪問的),提供 getter 和 setter 方法
2.6 GO 是如何實現繼承
將一個已存在類型作爲另一個類型的字段即爲內嵌,如果內嵌類型是匿名字段的話,內嵌類型的方法則會"晉升"成爲外層類型的方法。此時,如外層類型有方法名和內嵌類型方法的方法名一致(返回值類型是否一致無所謂),則優先調用外層類型方法。
已存在類型稱爲 : 父類
外層類型稱爲 : 子類
三、二叉樹(遞歸結構體)
3.1 什麼是二叉樹
3.2 二叉樹的定義
3.3 二叉樹遍歷
3.3.1 前序遍歷
3.3.2 中序遍歷
3.3.3 後序遍歷
一、結構體
GO 語言中沒有類的概念,因此在 GO 語言中結構體有着更爲重要的地位。一個自定義類型由一系列屬性組成,每個屬性都有自己的類型和值。結構體的目的就是把數據聚集在一起,以便能夠更加快捷地操作這些數據,在外部看來就像是一個實體。結構體是值類型。
1.1 如何定義結構體
結構體定義的方式一般如下,組成結構體類型的數據稱爲 字段。每個字段名字唯一且都有類型。字段類型可以是任意類型interface{}
。字段名大寫開頭表示 public,小寫開頭表示 private。
type Person struct{
Name string
Age int
...
}
1.2 如何創建結構體
1.2.1 使用 new
一般在結構體定義之後,使用 t := new(T)
給該結構體變量分配內存,返回指向已分配內存的指針,變量 t 是一個指向 T 的實例的指針,結構體字段的初始值是所屬類型的零值。
func main() {
jack := new(Person)
//Name = "",Age = 0
fmt.Printf("Name = %q,Age = %d\n",jack.Name,jack.Age)
}
1.2.2 使用字面量
初始化一個結構體也可以通過結構體字面量來實現。
func structLiteral() {
//注意 : 必須以字段在結構體定義時的順序寫
rose := Person{"rose",15}
//字段名加一個冒號,值的順序可以不一致,可以初始化部分字段的值(跟數組一樣)
jack := Person{Name: "jack",Age: 15}
tom := Person{Age: 22}
//Name = "jack",Age = 15
fmt.Printf("Name = %q,Age = %d\n",jack.Name,jack.Age)
//Name = "",Age = 22
fmt.Printf("Name = %q,Age = %d\n",tom.Name,tom.Age)
//Name = "rose",Age = 15
fmt.Printf("Name = %q,Age = %d\n",rose.Name,rose.Age)
}
1.2.3 聲明
如果聲明 var t T
,那麼也會給 t
分配內存,並初始化零值,這個時候 t
是類型 T 。
//聲明一個結構體
func interfacesturct() {
var jack Person
fmt.Printf("Name = %q,Age = %d\n",jack.Name,jack.Age)
}
func main() {
//Name = "",Age = 0
interfacesturct()
}
1.3 訪問結構體中的成員
**在 GO 語言中,使用 點語法給字段賦值,同理,也可以使用點語法獲取字段的值。**這個 " . " 在 GO 語言中叫做 選擇器。使用 t := new(T)
創建的結構體,其實 t
是一個結構體指針,但是同樣可以使用 點語法 直接獲取結構體的 字段,不必要像 c++那樣使用 ->
操作符,GO 語言會自動做轉換。GO 語言的結構體與它的數據在內存中是以連續內存存在的,即便是嵌套結構體也是一樣的。(這裏也可以總結一點:使用new(T)
得到的變量是對應 T 類型的指針)
func visitStructField() {
jack := new(Person)
//結構體指針指針使用 . 語言直接給結構體變量賦值
jack.Name = "jack"
//也可將結構體指針轉化成值類型再使用 . 語法給結構體變量賦值
(*jack).Age = 15
fmt.Printf("jack.Name = %s,jack.Age = %d\n",jack.Name,jack.Age)
}
1.4 自定義包的結構體
結構體類型再定義它的包中必須是唯一的,它的完全類型命名是:pack.struct 形式。
一個目錄下只能有一個包,包名和目錄名可以不一樣。
後面的章節會講到關於包的概念。
如果我們將 上面定義的 Peson 結構體放在 一個 Man 包中的話,那此時結構體的完整類型就變成了 “包名.結構體名”,即 Man.Person
package Man
type Person struct {
Name string
Age int
}
將創建結構體的代碼改一改
func newStruct() {
jack := new(Man.Person)
fmt.Printf("Name = %q,Age = %d\n",jack.Name,jack.Age)
}
1.5 結構體作爲參數傳輸
我們可以像其他數據類型一樣將結構體類型作爲參數傳遞給函數,訪問結構體變量,傳遞結構體變量有形式參數傳遞和指針參數傳遞兩種方式。
1.5.1 形式參數傳輸
傳遞副本,操作副本,不影響原對象
func operateSturct1(person Man.Person) {
person.Age = 22
}
1.5.2 指針參數傳輸
傳遞指針,操作原對象,影響原對象狀態
func operateStruct2(person *Man.Person) {
person.Age = 26
}
調用
func structAsParams() {
jack := new(Man.Person)
jack.Name = "jack"
jack.Age = 15
//作爲值類型傳遞,函數操作的是副本
operateSturct1(*jack)
fmt.Printf("jack.Age = %d\n",jack.Age)
//作爲指針參數傳遞,操作原對象
operateStruct2(jack)
fmt.Printf("jack.Age = %d\n",jack.Age)
}
func main() {
structAsParams()
//jack.Age = 15
//jack.Age = 26
}
1.6 帶標籤的結構體
結構體中的字段不僅有名字和類型,還有一個可選的附屬字段的標籤: 用於文檔說明或者開發過工程中的特殊標記,他的值是一串字符串。標籤內容只有refect
(反射機制)包能獲取。如下所示 :
姓名
是字段 Name
的附屬標籤(單引號支持跨行,不解析轉義字符)
年齡
是字段Age
的附屬標籤(雙引號僅支持一行,解析轉義字符)
package Man
type Person struct {
Name string `姓名`
Age int "年齡"
}
下面演示如何獲取標籤內容
func tagFieldStruct() {
jack := new(Man.Person)
jack.Name = "jack"
jack.Age = 15
//獲取 jack 的值
values := reflect.ValueOf(*jack)
// 獲取 jack 的類型
types := reflect.TypeOf(*jack)
// 獲取字段數量(私有+共有+匿名變量總和)
counts := values.NumField()
for index := 0;index < counts;index++ {
//獲取每一個字段
objectField := types.Field(index)
//獲取附屬於當前字段的標籤
tag := objectField.Tag
fmt.Println(tag)
//姓名
//年齡
}
}
1.7 匿名字段
結構體可以包含一個或多個匿名字段,但是每一種數據類型只能有一個匿名字段。匿名字段即字段沒有顯示的名字,只有字段對應的數據類型,此時類型就是字段的名字。此時,也遵循小寫開頭 private, 大寫開頭爲 public。
type Goods struct {
Name string "商品名字"
price float64 "商品價格"
int "商品編號"
}
//匿名字段
func anonymousField() {
// goods 爲結構體指針變量
goods := new(Goods)
goods.Name = "鯡魚罐頭"
goods.price = 89.5
goods.int = 20200456
fmt.Println(*goods)
}
1.8 內嵌結構體
數組從某種意義上來說也算是結構體體,index 相當於 字段。
GO 語言沒有繼承和多態,只有封裝。
結構體也是一種數據類型,所以它同樣可以作爲匿名字段使用。結構體可以被內嵌(或組合)入結構體當成一個字段使用,當內嵌結構體的時候,訪問字段仍然使用 點語法。模擬繼承。
//組合結構體
type admin struct {
user Man.Person
level string
}
//繼承
func inheritance() {
// 1.作爲值類型
var tom admin
tom.user.Name = "tom"
tom.level = "PM"
fmt.Println(tom) //{{tom 0} PM}
// 2.作爲字面量
jack := admin{
//注意: 跨行初始化需要用 ","結尾,單行不需要
user: Man.Person{
Name: "jack",
Age: 25,
},
//user: Man.Person{Name: "jack", Age: 25},
level: "CTO",
}
fmt.Println(jack) //{{jack 25} CTO}
// 3.作爲指針
rose := new(admin)
rose.user.Name = "rose"
rose.user.Age = 25
rose.level = "CEO"
fmt.Println(*rose) //{{rose 25} CEO}
}
1.9 結構體存放在數組或切片中
func structslices() {
// 數組(可以省略結構體前面的 Man.Person)
arrays := []Man.Person{
{
Name: "jack",
Age: 15,
},
{
Name: "rose",
Age: 23,
},
}
//arrays = [{jack 15} {rose 23}]
fmt.Printf("arrays = %v\n",arrays)
// 切片
slice := make([]Man.Person,0)
slice = append(slice, Man.Person{Name: "jack",Age: 23})
//slice = [{jack 23}]
fmt.Printf("slice = %v\n",slice)
}
1.10 值語義和引用語義
值語義和引用語義區別在於賦值。
b = a
b.Modify()
如果 b 的修改不會影響 a 的值,那麼此類型屬於值類型,也就意味着: b 會完全拷貝 a,成爲 a 的一個副本。
反之,就是引用類型。
數組、切片、結構體 都是值類型。
GO 語言中類型的值語義表現非常徹底,這也就意味着在調用標準庫的時候,更多的是傳入值語義類型的參數(這句話僅供參考)。
引用傳遞,傳遞的是一個指針。指針的值被複制,但指針的值指向的地址上的那個值不會被複制(被複制的是指針,但是兩個指針指向同一個實際的值),如果修改這個指針的值,實際上意味着這個值所指向的地址上的值被修改。(其實,指針也是變量類型,有自己的地址和值,通常指針的值指向一個變量的地址,所以說到底還是值傳遞)
二、方法
在 GO 語言中,方法和函數不是同一個東西,有明確的區分。方法有接收者,函數沒有接收者。接收者如何理解呢,也很簡單,有語言基礎的應該能明白。我們日常開發中,例如 定義 人,人有姓名,年齡。這個屬於屬性,說話聊天這個屬於動作。如果用 class 來定義的話,寫下 Java 代碼:
class Person{
//姓名
public String name;
//年齡
public int age;
//說話
public void speak() {
System.out.println("說話");
}
}
//main 方法
public class Test {
public static void main(String[] args) {
Person jack = new Person();
jack.name = "jack";
jack.age = 23;
jack.speak();
}
}
如上: jack 就是方法接收者,相當於給 jack 發送 speak() 消息, 只有 Person 類型的實例對象才具備調用資格。
類型 + 類型方法 = 面向對象中的一個類。
在 GO 語言中,類型的代碼和他相關的方法代碼可以分佈在同一個包下的不同源文件。方法不允許重載。
2.1 方法聲明和調用
2.1.1 方法聲明
方法的定義如下格式,在 方法名之前, func 關鍵字 之後的括號中指定接收者。
func (receiver type) methodName(arg1 type,...)(ouputValue1 type,...){
//TO DO
}
2.1.2 方法調用
如果 receiver
是接收者的一個實例,methodName
是接收者類型的一個方法名,那麼方法的調用遵循傳統的選擇器符號receiver.methodName()
。
如果 receiver 是一個指針,實例是值類型,receiver.methodName 和(&receiver).methodName 是等價的
如果 receiver 是一個值,實例是引用傳遞,receiver.methodName 和 (*receiver).methodName 也是等價的
結構體定義如下:
package Man
import "fmt"
type Person struct {
Name string `姓名`
Age int "年齡"
}
func (receiver *Person) Speak(){
receiver.Age = 28
fmt.Printf("%s 說話\n",receiver.Name)
}
func (receiver Person) Eat() {
receiver.Age = 28
fmt.Printf("%s 喫飯 \n",receiver.Name)
}
調用:
func callStructmethod() {
//指針類型
jack := new(Man.Person)
jack.Name = "jack"
jack.Age = 15
jack.Speak()
fmt.Println(*jack)
// 值類型
rose := Man.Person{
Name: "rose",
Age: 15,
}
rose.Eat()
fmt.Println(rose)
}
輸出結果:
jack 說話
{jack 28}
rose 喫飯
{rose 15}
2.1.3 值接收者 VS 指針接收者
從上面的例子打印結果來看,分別在 指針接收者的 Speak()
中修改接收者的值,結果值給修改。而 值接收者 Eat()
也修改了接收者的值,但是原實例的值卻未得到修改,說明操作的是其副本。綜上,我們也可得到如下結論:
要改變內容必須只用指針接收者
結構體過大也考慮使用指針接收者
一致性: 如有指針接收者,最好都是指針接收者
2.2 方法和函數的區別
其實,就是聲明上格式的區別而已。
2.3 如何擴展任意類型的方法
GO 語言中的大多數類型都是值語義,並且都可以包含對應的操作方法。在需要的時候,你可以給任意類型"增加"新方法。GO 開發中,一般有以下兩種方式爲已有類型或自定義類型添加新的方法: 定義別名 和 組合 。
2.3.1 定義別名
隊列 :先進先出
type Queue []interface{}
//加入隊列
func (q *Queue) Push(anyObject interface{}) (status bool){
if anyObject != nil{
*q = append(*q,anyObject)
return true
}
return false
}
//踢出隊列
func (q *Queue) Pop()(object interface{}) {
if len(*q) > 0 {
header := (*q)[0]
*q = (*q)[1:]
return header
}
return nil
}
//判斷隊列是否爲空
func (q *Queue) IsEmpty() (isEmpty bool) {
return len(*q) == 0
}
//隊列長度
func (q *Queue) Size() (size int) {
return len(*q)
}
//main 方法
func main() {
queues := make(Queue,0)
queues.Push(1)
queues.Push(2)
queues.Push(3)
fmt.Printf("queue size = %d\n",queues.Size())
fmt.Printf("queue is Empty = %t\n",queues.IsEmpty())
fmt.Printf("queue object is = %v\n",queues.Pop())
fmt.Printf("queue object is = %v\n",queues.Pop())
fmt.Printf("queue object is = %v\n",queues.Pop())
fmt.Printf("queue is Empty = %t\n",queues.IsEmpty())
/* 輸出結果:
queue size = 3
queue is Empty = false
queue object is = 1
queue object is = 2
queue object is = 3
queue is Empty = true
*/
}
棧結構 : 後進先出
/*
定義類型別名,模擬棧結構: 先進後出
*/
type Stack []interface{}
//入棧
func (stack *Stack) Push(anyObject interface{})(status bool) {
if anyObject != nil{
*stack = append(*stack,anyObject)
return true
}
return false
}
//出棧
func (stack *Stack) Pop()(object interface{}) {
if !stack.IsEmpty() {
value := (*stack)[len(*stack)-1]
*stack = (*stack)[:len(*stack)-1]
return value
}
return nil
}
//判斷是否爲空
func (stack *Stack) IsEmpty() bool {
return len(*stack) == 0
}
//棧中對象數量
func (stack *Stack) Size() int {
return len(*stack)
}
//main方法
func main() {
stack := make(Stack,0)
stack.Push(1)
stack.Push(2)
stack.Push(3)
fmt.Printf("stack size = %d\n",stack.Size())
fmt.Printf("stack po object = %d\n",stack.Pop())
fmt.Printf("stack po object = %d\n",stack.Pop())
fmt.Printf("stack po object = %d\n",stack.Pop())
fmt.Printf("stack is empty = %t\n",stack.IsEmpty())
}
/*輸出結果:
stack size = 3
stack po object = 3
stack po object = 2
stack po object = 1
stack is empty = true
*/
2.3.2 使用組合
使用內嵌結構體來實現
type admin struct {
user Man.Person
level string
}
2.4 工廠模式
在面向對象編程中,通過使用構造方法(GO 中沒有構造方法) 實現工廠模式。在 GO 中,也提供了類似的解決方案。以結構體爲例
type File struct{
fb int "文件描述符"
name string "文件名字"
}
下面是這個結構體類型對應的工廠方法,它返回一個結構體實例的指針
func NewFile(fb int,name string) *File{
if fb < 0 {
return nil
}
return &File{fb,name}
}
2.5 未導出字段
當類型被明確導出時,假設存在私有字段(小寫開頭)它的字段並沒有全部被導出,想在另一個程序中訪問未導出字段可以通過提供 getter 和 setter 來完成。
type Person struct{
firstName string
lastName string
}
// setter
func (p *Peerson)SetterFirstName(newName string){
p.firstName = newName
}
// getter
func (p *Person)GetterFirstName() string{
return p.firstName
}
2.6 GO 是如何實現繼承
將一個已存在類型作爲另一個類型的字段即爲內嵌,如果內嵌類型是匿名字段的話,內嵌類型的方法則會"晉升"成爲外層類型的方法。此時,如外層類型有方法名和內嵌類型方法的方法名一致(返回值類型是否一致無所謂),則優先調用外層類型方法。
前置條件 : 內嵌必須是匿名字段 如果不是匿名字段的話,必須通過外層類型先行訪問該字段,再繼續點語法方法該字段的方法。
2.6.1 匿名字段
type Point struct {
x,y float64
Name string
}
func (p *Point) Abs() float64 {
return math.Sqrt(p.x*p.x+p.y*p.y)
}
type NamePoint struct {
Point //此處是匿名字段
}
func main() {
object := NamePoint{
Point:Point{
x :3,
y: 4,
Name:"go"
},
}
//因爲是匿名字段,所有可以直接調用 內嵌類型的方法和字段
fmt.Println(object.Abs(),object.Name)
//5 cherish
}
非匿名字段情況: 遵循一般訪問規則
type Point struct {
x,y float64
Name string
}
func (p *Point) Abs() float64 {
return math.Sqrt(p.x*p.x+p.y*p.y)
}
type NamePoint struct {
Field Point //顯式字段
}
func main() {
object := NamePoint{
Field:Point{
x :3,
y: 4,
Name:"hhaa",
},
}
//遵循 點語法規則
fmt.Println(object.Field.Abs(),object.Field.Name)
}
2.6.1 作爲匿名字段 且 外層類型定義了內嵌類型一樣的方法名
type Point struct {
x,y float64
Name string
}
func (p *Point) Abs() float64 {
return math.Sqrt(p.x*p.x+p.y*p.y)
}
type NamePoint struct {
Point //匿名字段
city string
}
//如果 NamePoint 定義了和內嵌類型 Point 同名方法(方法名一樣,返回值類型可一樣,可不一樣),則優先調用外層結構體的方法
// 則可以理解爲 NamePoint 繼承了 Point ,並覆寫了 Abs() 方法
// 根據面向對象的思路: 優先調用子類的方法
func (n *NamePoint) Abs() float64 {
//注意:這裏如果寫 n.Abs() 就是遞歸調用了
return n.Point.Abs() * 10
}
func main() {
object := NamePoint{
Point:Point{
x :3,
y: 4,
Name: "go",
},
city: "china",
}
//擁有訪問匿名結構體的方法和字段的資格
fmt.Println(object.Abs(),object.Name,object.city)
//50 go china
}
三、二叉樹(遞歸結構體)
3.1 什麼是二叉樹
二叉樹中每個節點最多能鏈接兩個節點: 左節點(leftTree
) 和 右節點(rightTree
),這兩個節點本身又可以有左右節點,以此類推。樹的頂層節點 叫做根節點(root
),底層沒有子節點的節點叫做葉子節點(leaves
),葉子節點的 leftTree
和 rightTree
指針值爲 nil
值。
3.2 二叉樹的定義
在 GO 語言中,可以用結構體定義二叉樹的結構:
type TreeNode struct {
leftTree *TreeNode "左節點"
value string "節點"
rightTree *TreeNode "右節點"
}
3.3 二叉樹遍歷
二叉樹的遍歷有前中後序遍歷 3 種方式。所謂的前中後序遍歷,指的是 節點的位置 分別在 前中後。即: 根在最前面、中間、最後面。左右遵循先左後右原則。 瞭解更多
用 GO 建立上圖二叉樹
//定義一個全局切片,裝載遍歷結果
var VisitSlice = make([]string,0)
//提供訪問節點的value,並加入切片
func (node TreeNode) visitValue() {
VisitSlice = append(VisitSlice,node.value)
}
func createTree() *TreeNode {
tree := new(TreeNode)
//根節點
tree.value = "A"
//左
tree.leftTree = &TreeNode{value:"B"}
tree.leftTree.leftTree = &TreeNode{leftTree: nil,value: "D",rightTree: nil}
tree.leftTree.rightTree = &TreeNode{leftTree: nil,value: "E",rightTree: nil}
//右
tree.rightTree = &TreeNode{value: "C"}
tree.rightTree.leftTree = &TreeNode{leftTree: nil,value: "F",rightTree: nil}
tree.rightTree.rightTree = &TreeNode{leftTree: nil,value: "G",rightTree: nil}
return tree
}
3.3.1 前序遍歷
//前序遍歷
func (node *TreeNode)firstVisit() {
if node == nil{
return
}
node.visitValue()
node.leftTree.firstVisit()
node.rightTree.firstVisit()
}
func main(){
tree := createTree()
tree.firstVisit()
// 前序遍歷:[A B D E C F G]
fmt.Printf("前序遍歷:%v\n",VisitSlice)
}
3.3.2 中序遍歷
//中序遍歷
func (node *TreeNode) middleVisit() {
if node == nil{
return
}
node.leftTree.middleVisit()
node.visitValue()
node.rightTree.middleVisit()
}
func main(){
tree := createTree()
tree.middleVisit()
//中序遍歷:[D B E A F C G]
fmt.Printf("中序遍歷:%v\n",VisitSlice)
}
3.3.3 後序遍歷
//後序遍歷
func (node *TreeNode) lastVisit() {
if node == nil{
return
}
node.leftTree.lastVisit()
node.rightTree.lastVisit()
node.visitValue()
}
func main(){
tree := createTree()
tree.lastVisit()
//後序遍歷:[D E B F G C A]
fmt.Printf("後序遍歷:%v\n",VisitSlice)
}
有問題請留言,謝謝。