Golang教程第10篇-struct、二叉树、方法

1、struct

1.1.声明

type stu Student {
field1 类型
field2 类型
}
看一个例子

package main

type Student struct {
    Name string
    Age int
    score float
}

func main() {
    var stu Student
    stu.Name="wang"
    stu.Age=18
    stu.score=80
    
    fmt.Println(stu)
}

1.2.内存布局

struct的字段内存布局是连续的,可以打出每个字段的内存位置

package main

type Student struct {
    Namr string
    Age int
    score float
}

func main() {
    var stu Student
    stu.Name="wang"
    stu.Age=18
    stu.score=80
    
    fmt.Println(stu)
    fmt.Printf("Name:%p\n",&stu.Name)
    fmt.Printf("Name:%p\n",&stu.Age)
    fmt.Printf("Name:%p\n",&stu.score)
}  

编译执行
11str

1.3.初始化

三种方式

  • var stu Student
  • var stu *Student = &Student{}
  • var stu *Student = new(Student)
package main

// Student is a struct
type Student struct {
    Name string
    Age int
    score float32
}

func main() {
    // 方式一
    var stu Student
    stu.Name="wang"
    stu.Age=18
    stu.score=80
    
    // 方式二
    var stu1 *Student = &Student{
        Name: "xi",
        Age: 19,
    }
    
    // 方式二
    var stu2 = Student{
        Name: "teng",
        Age: 90,
    }
    
    fmt.Println(stu.Name)
    fmt.Println(stu1.Name)
    fmt.Println(stu2.Name)
}

编译执行
11str1

1.4.链表

定义

type Student struct {
    Name string
    Next* Student
}

链表的每一节点都包含下一节点的地址,把所有的节点串联起来,第一个节点叫做链表头

节点插入
采用尾部插入法来实现多节点链表

package main

import "fmt"

type Student struct{
    Name string
    Age int
    score float32
    Next *Student
}

func Trans(p *Student) {
    for p !- nil {
        fmt.Println(*p)
        p = p.Next
    }
}

func main() {
    var head Student
    head.Name = "teng"
    head.Age = 33
    head.score = 100
    
    var stu1 Student
    stu1.Name = "shan"
    stu1.Age = 30
    stu1.score = 90
    
    head.Next = &stu1
    Trans(&head)
}

编译执行
11str2

上面的写法是一个节点一个节点插入的,每个节点的地址是已知的,所以可以使用循环来实现多节点插入

package main

import "fmt"

type Student struct{
    Name string
    Age int
    score float32
    Next *Student
}

func Trans(p *Student) {
    for p != nil {
        fmt.Println(*p)
        p = p.Next
    }
}

func InsertTail(q *Student) {
   var tail = q
    for i := 0;i < 10;i++ {
        var stu *Student = &Student{
            Name: fmt.sprintf("stu%d",i),
            Age: rand.Intn(100),
            score: rand.Float32() * 100,
        }
        tail.Next = stu
        tail = stu
        } 
}

func main() {
    var head Student
    head.Name = "teng"
    head.Age = 33
    head.score = 100
    
    InsertTail(&head)
    Trans(&head)
}

编译执行
11str3

当然除了尾部插入实现多个链表输出,也可以使用头部插入来实现

package main

import "fmt"

type Student struct{
    Name string
    Age int
    score float32
    Next *Student
}

func Trans(p *Student) {
    for p != nil {
        fmt.Println(*p)
        p = p.Next
    }
}


func InsertHead(r *Student) *Student {
      for i := 0;i < 10;i++ {
         var stu *Student = &Student{
             Name: fmt.sprintf("stu%d",i),
             Age: rand.Intn(100),
             score: rand.Float32() * 100,
         }
         stu.Next = r       
         r = stu
         } 
         return r
}

func main() {
    var head *Student = new(Student)
    head.Name = "teng"
    head.Age = 33
    head.score = 100
  
    Trans(InsertHead(head))
}

编译执行
11str4

如果不想使用返回值的话,可以使用如下的代码

package main

import "fmt"   

type Student struct {
    Name string
    Age int
    score float32
}

func Trans(p *Student) {
    for p != nil {
        fmt.Println(*p)
        p = p.Next
    }
}

func InsertHead(r **Student) {
    for i := 0;i < 10;i++ {
        var stu *Student = &Student{
            Name: fmt.Sprintf("stu%d",i),
            Age: rand.Intn(100),
            score: rand.Float32() * 100,
        }
        stu.Next = *r
        *r = stu  
    }
}

func main() {
    var head *Student = new(Student)
    head.Name = "wang"
    head.Age = 33  
    head.score = 99
    
    InsertHead(&head)
    Trans(head)
}

如果要删除其中某个节点,比如stu6

package main

import "fmt"   

type Student struct {
    Name string
    Age int
    score float32
    Next *Student
}

func Trans(p *Student) {
    for p != nil {
        fmt.Println(*p)
        p = p.Next
    }
}

func InsertHead(r **Student) {
    for i := 0;i < 10;i++ {
        var stu *Student = &Student{
            Name: fmt.Sprintf("stu%d",i),
            Age: rand.Intn(100),
            score: rand.Float32() * 100,
        }
        stu.Next = *r
        *r = stu  
    }
}  

func Delnode(m *Student) {
    var prev *Student = m    
    for m != nil {
        if m.Name == "stu6" {   // 过滤条件
            prev.Next = m.Next  // 删除动作
            break
        }
        prev = m  
        m = m.Next 
    }
}

func main() {
    var head *Student = new(Student)
    head.Name = "wang"
    head.Age = 33  
    head.score = 99
    
    InsertHead(&head)
    Trans(head)
    
    Delnode(head)
    Trans(head)
}

如果想插入节点,代码可以如下

package main

import "fmt"   

type Student struct {
    Name string
    Age int
    score float32
    Next *Student
}

func Trans(p *Student) {
    for p != nil {
        fmt.Println(*p)
        p = p.Next
    }
}

func InsertHead(r **Student) {
    for i := 0;i < 10;i++ {
        var stu *Student = &Student{
            Name: fmt.Sprintf("stu%d",i),
            Age: rand.Intn(100),
            score: rand.Float32() * 100,
        }
        stu.Next = *r
        *r = stu  
    }
}  

func Delnode(m *Student) {
    var prev *Student = m    
    for m != nil {
        if m.Name == "stu6" {   // 过滤条件
            prev.Next = m.Next  // 删除动作
            break
        }
        prev = m  
        m = m.Next 
    }
}

func Addnode(a *Student,b *Student) {
    for a != nil {
        if a.Name == "stu5" {
            b.Next = a.Next
            a.Next = b
            break
        }
        a = a.Next
    }
}

func main() {
    var head *Student = new(Student)
    head.Name = "wang"
    head.Age = 33  
    head.score = 99
    
    InsertHead(&head)
    Trans(head)
    
    Delnode(head)
    Trans(head)
    
    var addnode *Student = new(Student)
    addnode.Name = "mn"
    addnode.Age = 27
    addnode.score = 98
    
    Addnode(head,addnode)
    Trans(head)
}

2、二叉树

11zx

代码如下

package main

import "fmt"

type Student struct {
    Name string
    Age int
    score float32
    left *Student
    right *Student
}

func Trans(p *Student) {
    if p == nil {
        return
    }
    fmt.Println(*p)
    Trans(p.right)
    Trans(p.left)
}

func main() {
    var root *Student = new(Student)
    root.Name = "stu01"
    root.Age = 32
    root.score = 90
    
    var left1 *Student = new(Student) 
    left1.Name = "stu02"
    left1.Age = 33
    left1.score = 91
    root.left = left1
    
    var right1 *Student = new(Student)
    right1.Name = "stu04"
    right1.Age = 49
    right1.score = 78
    root.right = right1
    
    var left2 *Student = new(Student)
    left2.Name = "stu03"
    left2.Age = 50
    left2.score = 59
    left1.left = left2
    
    Trans(root)
   
}

3、结构体的强制转换

看下面的例子

package main

type Student struct {
    Name int
}

var Stu Student //alias

func main() {
    var a Student
    a = Student{20}
    fmt.Println(a)
    var b Stu
    a = b
    fmt.Println(a)
}

保存报错

cannot use a (type Student) as type Stu in assignment

虽然Stu和Student字段一样,但他们是两个不同的类型,不能强制赋值,如果要使用可以先进行转换 a = Student(b)

4、结构体中的tag

先来看一个例子

package main

import {
    "fmt"
    "encoding/json"
}

type Student struct {
        name string
        age int
        score int
}

func main() {
    var a Student = Student{
        name:"stu01",
        age:18,
        score:80,
    }
    data,err := json.Mashal(a)
    if err != nil {
        fmt.Println("json encode failed:",err)
        return 
    }
    fmt.Println(string(data))       
}

编译执行
{}

再来一段代码

package main

import (
	"encoding/json"
	"fmt"
)

type Student struct {
	Name  string
	Age   int
	Score int
}

func main() {
	var a Student = Student{
		Name:  "stu01",
		Age:   18,
		Score: 80,
	}
	data, err := json.Marshal(a)
	if err != nil {
		fmt.Println("json encode failed:", err)
		return
	}
	fmt.Println(string(data))
}

编译执行
{"Name":"stu01","Age":18,"Score":80}

再来一段代码

package main

import (
	"encoding/json"
	"fmt"
)

type Student struct {
	Name  string `json:"name"`
	Age   int    `json:"age"`
	Score int    `json:"score"`
}

func main() {
	var a Student = Student{
		Name:  "stu01",
		Age:   18,
		Score: 80,
	}
	data, err := json.Marshal(a)
	if err != nil {
		fmt.Println("json encode failed:", err)
		return
	}
	fmt.Println(string(data))
}

编译执行
{"name":"stu01","age":18,"score":80}

5、结构体中的匿名字段

如何访问结构体中的匿名字段

package main

import "fmt"

type Cart struct {
    name string
    age int
}

type Train struct {
    Cart
    int
    start time.Time
}

func main() {
    var t Train
    t.name = "train"
    t.age = 100
    t.int = 300
    
    fmt.Println(t)
    
}

编译执行
{{train 100} 300 {0 0 <nil>}}

等同于

package main

import "fmt"

type Cart struct {
    name string
    age int
}

type Train struct {
    Cart
    int
    start time.Time
}

func main() {
    var t Train
    t.Cart.name = "train"
    t.Cart.age = 100
    t.int = 300
    
    fmt.Println(t)   
}

如果上面的两个结构体中含有相同的字段age,会输出什么呢

package main

import (
	"fmt"
	"time"
)

type Cart struct {
	name string
	age  int
}

type Train struct {
	Cart
	int
	start time.Time
	age   int
}

func main() {
	var t Train
	t.name = "train"
	t.age = 100
	t.int = 300

	fmt.Println(t)
}

编译执行
{{train 0} 300 {0 0 <nil>} 100}

6、struct里方法的定义

package main 
import "fmt"

type Student struct {
    Name string
    Age int
    Score int
}

func (p Student) init(name string,age int,score int) {
    p.Name = name
    p.Age = age
    p.Score =score
    fmt.Println(p)
}

func main() {
    var stu Student
    stu.init("stu",10,20)
}

编译执行
{stu 10 20}

init方法就是作用与结构体变量p,stu这里就等于p

上面的代码是通过方法对结构体变量进行初始化,下面可以通过使用初始化的结构体变量进行赋值验证初始化是否生效

package main 
import "fmt"

type Student struct {
    Name string
    Age int
    Score int
}

func (p Student) init(name string,age int,score int) {
    p.Name = name
    p.Age = age
    p.Score =score
    fmt.Println(p)
}

func (p Studnet) get() Student {
    return p
}

func main() {
    var stu Student
    stu.init("stu",10,20)
    
    stu1 := stu.get()
    fmt.Println(stu1)
}

编译执行
{stu 10 20}
{ 0 0}

这与预期的结果不一样,说明stu的初始化没有成功,修改一下

package main 
import "fmt"

type Student struct {
    Name string
    Age int
    Score int
}

func (p *Student) init(name string,age int,score int) {
    p.Name = name
    p.Age = age
    p.Score =score
    fmt.Println(p)
}

func (p Studnet) get() Student {
    return p
}

func main() {
    var stu Student
    (&stu).init("stu",10,20)
    
    stu1 := stu.get()
    fmt.Println(stu1)
}

编译执行
&{stu 10 20}
{stu 10 20}

这里之所以要传入一个地址过去,是因为前面那个不合预期的代码中init方法里的p只是stu的一个副本,传入的值并没有生效,所以这里传入一个地址进去,但是在go里面针对结构体变量做了简化,可以将&stu写成stu,但是方法中init作用的而结构体变量p的类型需要写成指针形式*Student,也就是说在调用针对结构体的方法时根据结构体变量p的类型来自动判断stu传入地址类型还是值类型

其实任何一个自定义的类型都可以给它定义一个方法,来看下面一个例子


package main

import "fmt"

type integer int

func (p integer) print() {
    fmt.Println(p)
}

func main() {
    var a integer
    a = 100
    a.print()
}

编译执行输出
100

前面在给结构体变量定义方法时碰到变量类型是值类型还是指针类型的问题,这里同样会涉及到这个问题

package main

import "fmt"

type integer int

func (p integer) print() {
	fmt.Println(p)
}

func (p *integer) set(b integer) {
	*p = b
}

func main() {
	var a integer
	a = 100
	a.print()

	a.set(1000)
	a.print()
}

编译执行
100
1000

7、继承

go里的继承是通过匿名字段实现的

package main

import "fmt"

type Car struct {
    weight int
    name string
}

func (p *Car) Run() {
    fmt.Println("running")
}

type Bike struct {
    Car
    lunzi int
}

type Train struct {
    c Car
}

func main() {
    var a Bike
    a.weight = 100
    a.name = "bike"
    a.lunzi = 200
    a.Run()
    
    fmt.Println(a)
    
    var b Train
    b.c.weight = 300
    b.c.name = "train"
    b.Run()
  
}   

编译执行
running
{{100 bike} 200}
running