Go基础学习笔记(一):基础语法、内建容器、面向对象、依赖管理,2024年最新最新Golang架构师成长路线

43 阅读9分钟

slice本身没有数据,是对底层array的一个view


2)Slice的扩展



arr := [...]int{0, 1, 2, 3, 4, 5, 6, 7}
s1 := arr[2:6]
s2 := s1[3:5]
fmt.Println(s1) // [2 3 4 5]
fmt.Println(s2) // [5 6]

![](https://p3-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/3acb7707cc45448baf52f862a23a15c7~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5py65Zmo5a2m5Lmg5LmL5b-DQUk=:q75.awebp?rk3s=f64ab15b&x-expires=1771262198&x-signature=BTEA47IVPOX%2Bvd7ypZhlbliNBK0%3D)
Slice的实现:


![](https://p3-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/8ee3c887457447f3813a60bd32f0e32a~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5py65Zmo5a2m5Lmg5LmL5b-DQUk=:q75.awebp?rk3s=f64ab15b&x-expires=1771262198&x-signature=QzG9p81REgJQdLKko6LD8sjMnDs%3D)
* slice可以向后扩展,不可以向前扩展
* s[i]不可以超越len(s),向后扩展不可以超越底层数组cap(s)



arr := [...]int{0, 1, 2, 3, 4, 5, 6, 7}
s1 := arr[2:6]
s2 := s1[3:5]
fmt.Printf("s1=%v, len(s1)=%d, cap(s1)=%d\n", s1, len(s1), cap(s1)) // s1=[2 3 4 5], len(s1)=4, cap(s1)=6
fmt.Printf("s2=%v, len(s2)=%d, cap(s2)=%d\n", s2, len(s2), cap(s2)) // s2=[5 6], len(s2)=2, cap(s2)=3

##### 3)、切片的操作


1)向Slice添加元素



arr := [...]int{0, 1, 2, 3, 4, 5, 6, 7}
s1 := arr[2:6]
s2 := s1[3:5]
fmt.Printf("s1=%v, len(s1)=%d, cap(s1)=%d\n", s1, len(s1), cap(s1)) // s1=[2 3 4 5], len(s1)=4, cap(s1)=6
fmt.Printf("s2=%v, len(s2)=%d, cap(s2)=%d\n", s2, len(s2), cap(s2)) // s2=[5 6], len(s2)=2, cap(s2)=3
s3 := append(s2, 10)
s4 := append(s3, 11)
s5 := append(s4, 12)
fmt.Println(s3) // [5 6 10]
fmt.Println(s4) // [5 6 10 11]
fmt.Println(s5) // [5 6 10 11 12] s4 and s5 no longer view arr

* 添加元素时如果超过cap,系统会重新分配更大的底层数组
* 由于值传递的关系,必须接收append的返回值


2)Slice其他操作



func printSlice(s []int) { fmt.Printf("len=%d, cap=%d\n", len(s), cap(s)) }

func main() { // creating slice var s []int // zero value for slice is nil for i := 0; i < 100; i++ { printSlice(s) s = append(s, 2*i+1) } fmt.Println(s)

s1 := []int{2, 4, 6, 8}
printSlice(s1)

s2 := make([]int, 16)
printSlice(s2)

s3 := make([]int, 16, 32)
printSlice(s3) // len=16, cap=32

// copying slice
copy(s2, s1)
fmt.Println(s2) // [2 4 6 8 0 0 0 0 0 0 0 0 0 0 0 0]

// deleting elements from slice
s2 = append(s2[:3], s2[4:]...)
fmt.Println(s2) // [2 4 6 0 0 0 0 0 0 0 0 0 0 0 0]

// popping from front
front := s2[0]
s2 = s2[1:]
// popping from back
tail := s2[len(s2)-1]
s2 = s2[:len(s2)-1]
fmt.Println(front) // 2
fmt.Println(tail)  // 0
fmt.Println(s2)    // [4 6 0 0 0 0 0 0 0 0 0 0 0]

}


##### 4)、 Map



m := map[string]string{
	"one":   "java",
	"two":   "go",
	"three": "python",
}
m2 := make(map[string]int)
var m3 map[string]int
fmt.Println(m, m2, m3)

// traversing map
for k, v := range m {
	fmt.Println(k, v)
}

// getting values
one := m["one"]
fmt.Println(one)
if one2, ok := m["one"]; ok {
	fmt.Println(one2)
}

// deleting values
delete(m, "three")
fmt.Println(m)

1)Map的操作


* 创建:`make(map[string]int)`
* 获取元素:`m[key]`
* key不存在时,获得value类型的初始值
*`value, ok := m[key]`来判断是否存在key
* 用delete删除一个key


2)Map的遍历


* 使用range遍历key,或者遍历key value对
* 不保证遍历顺序,如需顺序,需手动对key排序
* 使用len获得元素个数


3)map的key


* map使用哈希表,必须可以比较相等
* 除了slice、map、function的内建类型都可以作为key
* struct类型不包含上述字段,也可以作为key


##### 5)、Map例题


例:[最长不含重复字符的子字符串](https://gitee.com/vip204888)


对于每一个字母x


* lastOccurred[x]不存在,或者<start -> 无需操作
* lastOccurred[x]>=start -> 更新start
* 更新lastOccurred[x],更新maxLength



func lengthOfLongestSubstring(s string) int { lastOccurred := make(map[byte]int) start, maxLength := 0, 0 for i, ch := range []byte(s) { lastI, ok := lastOccurred[ch] if ok && lastI >= start { start = lastOccurred[ch] + 1 } if i-start+1 > maxLength { maxLength = i - start + 1 } lastOccurred[ch] = i } return maxLength }


##### 6)、字符和字符串处理


1)rune相当于go的char



s := "你好世界!"
fmt.Println(len(s))    // 15
for i, ch := range s { // ch is a rune
	fmt.Printf("(%d %X)", i, ch) // (0 4F60)(3 597D)(6 4E16)(9 754C)(12 FF01)
}
fmt.Println()

fmt.Println(utf8.RuneCountInString(s)) // 5

bytes := []byte(s)
for len(bytes) > 0 {
	ch, size := utf8.DecodeRune(bytes)
	bytes = bytes[size:]
	fmt.Printf("%c", ch)
} // 你好世界!
fmt.Println()

for i, ch := range []rune(s) {
	fmt.Printf("(%d %c)", i, ch)
} // (0 你)(1 好)(2 世)(3 界)(4 !)
fmt.Println()

* 使用range遍历pos rune对
* 使用`utf8.RuneCountInString`获得字符数量
* 使用len获得字节长度
* 使用[]byte获得字节


[最长不含重复字符的子字符串](https://gitee.com/vip204888)支持中文



func lengthOfLongestSubstring(s string) int { lastOccurred := make(map[rune]int) start, maxLength := 0, 0 for i, ch := range []rune(s) { lastI, ok := lastOccurred[ch] if ok && lastI >= start { start = lastOccurred[ch] + 1 } if i-start+1 > maxLength { maxLength = i - start + 1 } lastOccurred[ch] = i } return maxLength }


2)其他字符串操作


* Fields,Split,Join
* Contains,Index
* ToLower,ToUpper
* Trim,TrimRight,TrimLeft


#### 3、面向对象


##### 1)、结构体和方法


1)面向对象


* Go语言仅支持封装,不支持继承和多态
* Go语言没有class,只有struct


2)结构的创建



type treeNode struct { value int left, right *treeNode }

func createNode(value int) *treeNode { return &treeNode{value: value} }

func main() { root := treeNode{value: 3} root.left = &treeNode{} root.right = &treeNode{5, nil, nil} root.right.left = new(treeNode) root.left.right = createNode(2) }


* 使用自定义工厂函数
* 注意返回了局部变量的地址


3)为结构定义方法



package main

import "fmt"

type treeNode struct { value int left, right *treeNode }

func (node treeNode) print() { fmt.Print(node.value, " ") }

func (node treeNode) setValue(value int) { node.value = value }

func (node *treeNode) setValue2(value int) { node.value = value }

// 中序遍历 func (node *treeNode) traverse() { if node == nil { return } node.left.traverse() node.print() node.right.traverse() }

func createNode(value int) *treeNode { return &treeNode{value: value} }

func main() { root := treeNode{value: 3} root.left = &treeNode{} root.right = &treeNode{5, nil, nil} root.right.left = new(treeNode) root.left.right = createNode(2) root.print() // 3 fmt.Println()

root.right.left.setValue(4)
root.right.left.print() // 0
root.right.left.setValue2(4)
root.right.left.print() // 4
fmt.Println()

root.traverse() // 0 2 3 4 5

}


* 显示定义和命名方法接收者
* 只有使用指针才可以改变结构内容
* nil指针也可以调用方法


4)值接收者 vs 指针接收者


* 要改变内容必须使用指针接收者
* 结构过大也考虑使用指针接收者
* 一致性:如有指针接收者,最好都是指针接收者
* 值接收者是Go语言特有
* 值/指针接收者均可接收值/指针


##### 2)、包和封装


1)封装


* 名字一般使用CamelCase
* 首字母大写:public
* 首字母小写:private


2)包


* 每个目录一个包
* main包包含可执行入口
* 为结构定义的方法必须放在同一个包内,可以是不同文件



go mod init tree



package node

import "fmt"

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

func (node TreeNode) Print() { fmt.Print(node.Value, " ") }

func (node TreeNode) SetValue(value int) { node.Value = value }

func (node *TreeNode) SetValue2(value int) { node.Value = value }

func CreateNode(value int) *TreeNode { return &TreeNode{Value: value} }



package node

// 中序遍历 func (node *TreeNode) Traverse() { if node == nil { return } node.Left.Traverse() node.Print() node.Right.Traverse() }



package main

import ( "fmt" "tree/node" )

func main() { root := node.TreeNode{Value: 3} root.Left = &node.TreeNode{} root.Right = &node.TreeNode{5, nil, nil} root.Right.Left = new(node.TreeNode) root.Left.Right = node.CreateNode(2) root.Print() // 3 fmt.Println()

root.Right.Left.SetValue(4)
root.Right.Left.Print() // 0
root.Right.Left.SetValue2(4)
root.Right.Left.Print() // 4
fmt.Println()

root.Traverse() // 0 2 3 4 5

}


##### 3)、扩展已有类型


如何扩充系统类型或者别人的类型


* 定义别名
* 使用组合


1)使用组合



package main

import ( "fmt" "tree/node" )

type myTreeNode struct { node *node.TreeNode }

// 后序遍历 func (myNode *myTreeNode) postOrder() { if myNode == nil || myNode.node == nil { return } left := myTreeNode{myNode.node.Left} left.postOrder() right := myTreeNode{myNode.node.Right} right.postOrder() myNode.node.Print() }

func main() { root := node.TreeNode{Value: 3} root.Left = &node.TreeNode{} root.Right = &node.TreeNode{5, nil, nil} root.Right.Left = new(node.TreeNode) root.Left.Right = node.CreateNode(2) root.Print() // 3 fmt.Println()

root.Right.Left.SetValue(4)
root.Right.Left.Print() // 0
root.Right.Left.SetValue2(4)
root.Right.Left.Print() // 4
fmt.Println()

root.Traverse() // 0 2 3 4 5
fmt.Println()

myRoot := myTreeNode{&root}
myRoot.postOrder() // 2 0 4 5 3

}


2)定义别名



go mod init queue



package queue

type Queue []int

func (q *Queue) Push(v int) { *q = append(*q, v) }

func (q *Queue) Pop() int { head := (*q)[0] *q = (*q)[1:] return head }

func (q *Queue) IsEmpty() bool { return len(*q) == 0 }



package main

import ( "fmt" "queue/queue" )

func main() { q := queue.Queue{1} q.Push(2) q.Push(3) fmt.Println(q.Pop()) // 1 fmt.Println(q.Pop()) // 2 fmt.Println(q.IsEmpty()) // false fmt.Println(q.Pop()) // 3 fmt.Println(q.IsEmpty()) // true }


##### 4)、使用内嵌来扩展已有类型



package main

import ( "fmt" "tree/node" )

type myTreeNode struct { *node.TreeNode // Embedding 内嵌 }

// 后序遍历 func (myNode *myTreeNode) postOrder() { if myNode == nil || myNode.TreeNode == nil { return } left := myTreeNode{myNode.Left} left.postOrder() right := myTreeNode{myNode.Right} right.postOrder() myNode.Print() }

func (myNode *myTreeNode) Traverse() { fmt.Println("This method is shadowed.") }

func main() { root := myTreeNode{&node.TreeNode{Value: 3}} root.Left = &node.TreeNode{} root.Right = &node.TreeNode{5, nil, nil} root.Right.Left = new(node.TreeNode) root.Left.Right = node.CreateNode(2) root.Print() // 3 fmt.Println()

root.Right.Left.SetValue(4)
root.Right.Left.Print() // 0
root.Right.Left.SetValue2(4)
root.Right.Left.Print() // 4
fmt.Println()

root.TreeNode.Traverse() // 0 2 3 4 5
fmt.Println()

root.Traverse() // This method is shadowed.

root.postOrder() // 2 0 4 5 3

}


#### 4、Go语言的依赖管理



##### 1)、依赖管理


依赖管理的三个阶段:GOPATH,GOVENDOR,go mod



##### 2)、go mod的使用



go mod init gomodtest



go get -u go.uber.org/zap


![](https://p3-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/c25888f1402e4c35b14af23517ce1e81~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5py65Zmo5a2m5Lmg5LmL5b-DQUk=:q75.awebp?rk3s=f64ab15b&x-expires=1771262198&x-signature=BxFSdL1sNz6mM0raEpYMGWPi1LQ%3D)
go.mod文件中指定了依赖库的版本,同时新增了一个go.sum的文件



package main

import "go.uber.org/zap"

func main() { logger, _ := zap.NewProduction() logger.Warn("warning test") }


指定版本:



go get -u go.uber.org/zap@v1.11


go mod:


* 由go命令统一的管理,用户不必关心目录结构
* 初始化:go mod init
* 增加依赖:go get
* 更新依赖:go get [@v…],go mod tidy


#### 5、面向接口


##### 1)、接口的概念



package infra

import ( "io/ioutil" "net/http" )

type Retriever struct { }

func (Retriever) Get(url string) string { resp, err := http.Get(url) if err != nil { panic(err) } defer resp.Body.Close() bytes, _ := ioutil.ReadAll(resp.Body) return string(bytes) }



package testing

type Retriever struct { }

func (Retriever) Get(url string) string { return "fake content" }



package main

import ( "fmt" "learngo/downloader/infra" "learngo/downloader/testing" )

func getTestingRetriever() retriever { return testing.Retriever{} }

func getInfraRetriever() retriever { return infra.Retriever{} }

type retriever interface { Get(url string) string }

func main() { url := "blog.csdn.net" fmt.Println(getInfraRetriever().Get(url)) fmt.Println(getTestingRetriever().Get(url)) }


##### 2)、duck typing的概念


* 像鸭子走路,像鸭子叫(长得像鸭子),那么就是鸭子
* 描述事物的外部行为而非内部结构
* 严格说Go语言属于结构化类型系统,类似duck typing


##### 3)、接口的定义和实现


1)接口的定义


![](https://p3-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/5693546433544b50924b1ca664e5b00c~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5py65Zmo5a2m5Lmg5LmL5b-DQUk=:q75.awebp?rk3s=f64ab15b&x-expires=1771262198&x-signature=4JHU3GPyLxM0w%2BETO%2Ful5Cl6L0M%3D)
* 接口由使用者定义


2)接口的实现


* 接口的实现是隐式的
* 只要实现接口里的方法



package mock

type Retriever struct { Contents string }

func (r Retriever) Get(url string) string { return r.Contents }



package real

import ( "net/http" "net/http/httputil" "time" )

type Retriever struct { UserAgent string TimeOut time.Duration }

func (r Retriever) Get(url string) string { resp, err := http.Get(url) if err != nil { panic(err) } result, err := httputil.DumpResponse(resp, true) defer resp.Body.Close() if err != nil { panic(err) } return string(result) }



package main

import ( "fmt" "learngo/retriever/mock" "learngo/retriever/real" )

type Retriever interface { Get(url string) string }

func download(r Retriever) string { return r.Get("blog.csdn.net") }

func main() { var r Retriever r = real.Retriever{} fmt.Println(download(r))

r = mock.Retriever{"this is a fake blog.csdn.net"}
fmt.Println(download(r))

}


##### 4)、接口的值类型



package mock

type Retriever struct { Contents string }

func (r Retriever) Get(url string) string { return r.Contents }



package real

import ( "net/http" "net/http/httputil" "time" )

type Retriever struct { UserAgent string TimeOut time.Duration }

func (r *Retriever) Get(url string) string { resp, err := http.Get(url) if err != nil { panic(err) } result, err := httputil.DumpResponse(resp, true) defer resp.Body.Close() if err != nil { panic(err) } return string(result) }



package main

import ( "fmt" "learngo/retriever/mock" "learngo/retriever/real" "time" )

type Retriever interface { Get(url string) string }

func download(r Retriever) string { return r.Get("blog.csdn.net") }

func inspect(r Retriever) { switch v := r.(type) { case mock.Retriever: fmt.Println("Contents:", v.Contents) case *real.Retriever: fmt.Println("UserAgent:", v.UserAgent) } }

func main() { var r Retriever r = mock.Retriever{Contents: "this is a fake blog.csdn.net"} fmt.Printf("%T %v\n", r, r) // mock.Retriever {this is a fake blog.csdn.net} inspect(r) // Contents: this is a fake blog.csdn.net

r = &real.Retriever{UserAgent: "Mozilla/5.0", TimeOut: time.Minute}
fmt.Printf("%T %v\n", r, r) // \*real.Retriever &{Mozilla/5.0 1m0s}
inspect(r)                  // UserAgent: Mozilla/5.0

// Type assertion
realRetriever := r.(\*real.Retriever)
fmt.Println(realRetriever.TimeOut)

if mockRetriever, ok := r.(mock.Retriever); ok {
	fmt.Println(mockRetriever.Contents)
} else {
	fmt.Println("not a mock retriever")
}

}


1)接口变量里面有什么


![](https://p3-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/4f39bde00c4c4edb9dc27b4330e9d2ab~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5py65Zmo5a2m5Lmg5LmL5b-DQUk=:q75.awebp?rk3s=f64ab15b&x-expires=1771262198&x-signature=ubh3rhVJRIIFDYDFQ0Lw8tJ3Bw8%3D)
![](https://p3-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/f79d552ba6da4ec9b8c9383c19ef2862~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5py65Zmo5a2m5Lmg5LmL5b-DQUk=:q75.awebp?rk3s=f64ab15b&x-expires=1771262198&x-signature=nOSTGLqO4ijNQIlufl3B1H8MGwU%3D)
* 接口变量自带指针
* 接口变量同样采用值传递,几乎不需要使用接口的指针
* 指针接收者实现只能以指针方式使用;值接收者都可以


2)查看接口变量


* 表示任何类型:interface{}
* Type Assertion
* Type Switch



package queue

type Queue []interface{}

func (q *Queue) Push(v interface{}) { *q = append(*q, v) }

func (q *Queue) Pop() interface{} { head := (*q)[0] *q = (*q)[1:] return head }

func (q *Queue) IsEmpty() bool { return len(*q) == 0 }



package main

import ( "fmt" "learngo/queue/queue" )

func main() { q := queue.Queue{1} q.Push(2) q.Push(3) fmt.Println(q.Pop()) // 1 fmt.Println(q.Pop()) // 2 fmt.Println(q.IsEmpty()) // false fmt.Println(q.Pop()) // 3 fmt.Println(q.IsEmpty()) // true q.Push("abc") fmt.Println(q.Pop()) // abc }


##### 5)、接口的组合



package mock

type Retriever struct { Contents string }

func (r *Retriever) Get(url string) string { return r.Contents }

func (r *Retriever) Post(url string, form map[string]string) string { r.Contents = form["contents"] return "ok" }



package main

import ( "fmt" "learngo/retriever/mock" "learngo/retriever/real" "time" )

const url = "blog.csdn.net"

type Retriever interface { Get(url string) string }

type Poster interface { Post(url string, form map[string]string) string }

func download(r Retriever) string { return r.Get(url) }

func post(poster Poster) { poster.Post(url, map[string]string{ "name": "xiaoming", }) }

type RetrieverPoster interface { Retriever Poster }

func session(s RetrieverPoster) string { s.Post(url, map[string]string{ "contents": "another fake blog.csdn.net", }) return s.Get(url) }

func inspect(r Retriever) { switch v := r.(type) { case *mock.Retriever: fmt.Println("Contents:", v.Contents) case *real.Retriever: fmt.Println("UserAgent:", v.UserAgent) } }

func main() { var r Retriever retriever := mock.Retriever{Contents: "this is a fake blog.csdn.net"} r = &retriever fmt.Printf("%T %v\n", r, r) // *mock.Retriever &{this is a fake blog.csdn.net} inspect(r) // Contents: this is a fake blog.csdn.net

r = &real.Retriever{UserAgent: "Mozilla/5.0", TimeOut: time.Minute}
fmt.Printf("%T %v\n", r, r) // \*real.Retriever &{Mozilla/5.0 1m0s}
inspect(r)                  // UserAgent: Mozilla/5.0

// Type assertion
realRetriever := r.(\*real.Retriever)
fmt.Println(realRetriever.TimeOut)

if mockRetriever, ok := r.(\*mock.Retriever); ok {
	fmt.Println(mockRetriever.Contents)
} else {
	fmt.Println("not a mock retriever")
}

fmt.Println(session(&retriever)) // another fake blog.csdn.net

}


##### 6)、常用系统接口


* Stringer
* Reader/Writer


#### 6、函数式编程


##### 1)、函数式编程


1)函数式编程 vs 函数指针


* 函数是一等公民:参数、变量、返回值都可以是函数
* 高阶函数
* 函数 -> 闭包


2)正统函数式编程


* 不可变性:不能有状态,只有常量和函数
* 函数只能有一个参数


3)闭包


![](https://p3-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/4d721014f6d34c7cb090626b5d0b5431~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5py65Zmo5a2m5Lmg5LmL5b-DQUk=:q75.awebp?rk3s=f64ab15b&x-expires=1771262198&x-signature=lGbFAoG04ol3w8r3BOdyOOFnLaQ%3D)

func adder() func(int) int { sum := 0 return func(v int) int { sum += v return sum } }

func main() { a := adder() for i := 0; i < 10; i++ { fmt.Printf("0 + 1 + ... + %d = %d\n", i, a(i)) } }


##### 2)、函数式编程示例


1)斐波那契数列



func fibonacci() func() int { a, b := 0, 1 return func() int { a, b = b, a+b return a } }

func main() { f := fibonacci() for i := 0; i < 10; i++ { fmt.Println(f()) } }


2)为函数实现接口



func fibonacci() intGen { a, b := 0, 1 return func() int { a, b = b, a+b return a } }

type intGen func() int

func (g intGen) Read(p []byte) (n int, err error) { next := g() if next > 1000 { return 0, io.EOF } s := fmt.Sprintf("%d\n", next) return strings.NewReader(s).Read(p) }

func printFileContents(reader io.Reader) { scanner := bufio.NewScanner(reader) for scanner.Scan() { fmt.Println(scanner.Text()) } }

func main() { f := fibonacci() printFileContents(f) }


3)使用函数遍历二叉树



package node

import "fmt"

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

func (node TreeNode) Print() { fmt.Print(node.Value, " ") }

func CreateNode(value int) *TreeNode { return &TreeNode{Value: value} }



package node

import "fmt"

func (node *TreeNode) Traverse() { node.TraverseFunc(func(node *TreeNode) { node.Print() }) fmt.Println() }

// 中序遍历 func (node *TreeNode) TraverseFunc(f func(*TreeNode)) { if node == nil { return } node.Left.TraverseFunc(f) f(node) node.Right.TraverseFunc(f) }



package main

import ( "fmt" "learngo/tree/node" )

func main() { root := node.CreateNode(3) root.Left = node.CreateNode(0) root.Right = node.CreateNode(5) root.Right.Left = node.CreateNode(4) root.Left.Right = node.CreateNode(2)

img img img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Go语言开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以戳这里获取