结构体:
1、用来自定义复杂数据结构
2、struct里面可以包含多个字段(属性)
3、struct类型可以定义方法,注意和函数的区分
4、strucr类型是值类型
5、struct类型可以嵌套
6、go语言中没有class类型,只有struct类型
struct声明:
type 标识符 struct{
field1 type
field2 type
}
例子:
type Student struct{
Name string
Age int
Score int
}
struct中字段访问,和其他语言一样,使用点
例子:
var stu Student //拿结构题定义一个变量
stu.Name=”tony”
stu.Age=18
stu.Score=20
fmt.Printf(“name=%s,age=%d,score=%d”,stu.Name,stu.Age,stu.Sore)
struct定义的三种形式 初始化的三种方式
a、var stu Student
b、var stu *Student=new(Student)
c、var stu *Student=&Student{}
其中b和c返回的都是指向结构体的指针,访问形式如下:
a、stu.Name、stu.Age 和stu.Score 或者(*stu).Name、 (*stu).Age等
如果是指针形式可以用上面的普通的方式访问,其实就自动转化为指针访问的形式
package main
import (
"fmt"
)
type Student struct{
Name string
Age int
score float32
}
func main(){
//声明方式一
var stu Student
stu.Name="hua"
stu.Age=18
stu.score=80
//声明方式二
var stu1 *Student =&Student{
Age:20,
Name:"hua",
}
//声明方式三
var stu3 =Student{
Age:20,
Name:"hua",
}
fmt.Printf(stu1.Name)
fmt.Printf(stu3.Name)
}
struct内存布局
例子:
package main
import(
"fmt"
)
type Student struct{
Name string
Age int
score float32
}
func main(){
var stu Student
stu.Name="hua"
stu.Age=18
stu.score=80
fmt.Print(stu)
fmt.Printf("Name:%p\n",&stu.Name)
fmt.Printf("Age:%p\n",&stu.Age)
fmt.Printf("score:%p\n",&stu.score)
}
{hua 18 80}Name:0xc04204a3a0
Age:0xc04204a3b0
score:0xc04204a3b8
这里int32是4字节,64是8字节
链表的定义:
type Student struct{
name string
next* Student
}
每个节点包含下一个节点的地址,这样把所有的节点串起来,通常把链表中的每一个节点叫做链表头
遍历到最后一个元素的时候有个特点,就是next这个指针指向的是nil,可以从这个特点来判断是否是链表结束
单链表的特点:只有一个字段指向后面的结构体
单链表只能从前往后遍历
双链表的特点:有两个字段,分别指向前面和后面的结构体
双链表可以双向遍历
链表操作:
1、生成链表及遍历链表操作
package main
import (
"fmt"
)
type Student struct{
Name string
Age int
Score float32
next *Student
}
func main(){
var head Student
head.Name="hua"
head.Age=18
head.Score=80
var stu1 Student
stu1.Name="stu1"
stu1.Age=20
stu1.Score=100
head.next=&stu1
//遍历
var p *Student=&head //生成p指针,指向head
for p!=nil{ //这里p就是head结构体,所以要从第一个遍历
fmt.Println(*p)
p=p.next
}
}
D:\project>go build go_dev / example/example3
D:\project>example3.exe
{hua 18 80 0xc042078060} 这里第三个值指向的是下一个结构体
{stu1 20 100 <nil>}
上面的程序不规范,修改如下:
package main
import (
"fmt"
)
type Student struct{
Name string
Age int
Score float32
next *Student
}
func trans(p *Student){
for p!=nil { //这里p就是head结构体,所以要从第一个遍历
fmt.Println(*p)
p = p.next
}
}
func main(){
var head Student
head.Name="hua"
head.Age=18
head.Score=80
var stu1 Student
stu1.Name="stu1"
stu1.Age=20
stu1.Score=100 //这里默认第二个链表为nil
head.next=&stu1
//var p *Student=&head
trans(&head) //生成p指针,指向head
}
插入链表的方法:
1、尾部插入法,就在链表的尾部插入结构体
代码如下:
package main
import(
"fmt"
)
type Student struct{
Name string
Age int
Score float32
next *Student
}
func trans(p *Student){
for p!=nil { //这里p就是head结构体,所以要从第一个遍历
fmt.Println(*p)
p = p.next
}
}
func main() {
var head Student
head.Name = "hua"
head.Age = 18
head.Score = 80
var stu1 Student
stu1.Name = "stu1"
stu1.Age = 20
stu1.Score = 100
var stu2 Student
stu2.Name="stu2"
stu2.Age=22
stu2.Score=90
head.next=&stu1
stu1.next=&stu2
trans(&head)
}
2、尾部循环插入
package main
import (
"fmt"
"math/rand"
)
type Student struct{
Name string
Age int
Score float32
next *Student
}
func trans(p *Student){
for p!=nil { //这里p就是head结构体,所以要从第一个遍历
fmt.Println(*p)
p = p.next
}
}
//尾部循环插入数据
func trans2(tail *Student){
for i:=0;i<10;i++{
stu:=&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="hua"
head.Age=18
head.Score=100
//下面这两个都是根据head这个链表结构体产生
trans2(&head)
trans(&head)
}
3、头部插入
1、注意给指针分配内存空间
2、用指针的方式才可以分配内存,如果用变量就不行(牵扯到地址就用指针)
package main
import(
"fmt"
"math/rand"
)
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=&Student{}
var head *Student=new(Student)
head.Name="hua"
head.Age=18
head.Score=100
//从头插入
for i:=0;i<10;i++{
stu:=Student{
Name:fmt.Sprintf("stu%d",i),
Age:rand.Intn(100),
Score:rand.Float32()*100,
}
//头部插入必须要传递指针才可以,首先头部插入一个,链表指向第一个,但是插入的链表地址被覆盖
stu.next=head
head=&stu //指针赋值
}
trans(head)
}
头部插入和尾部插入的区别是
头部插入:需要用指针的方式来插入
尾部插入:直接插入就可以了(变量形式插入)
优化代码
package main
import(
"fmt"
"math/rand"
)
type Student struct{
Name string
Age int
Score float32
next *Student
}
//打印函数
func trans(p *Student){
for p!=nil{
fmt.Println(*p)
p=p.next
}
}
//这里的head是指针变量副本,这里要接受指针的指针,head1 * Student是指针变量的副本
func insertHead(head1 **Student){
//从头插入
for i:=0;i<10;i++ {
stu := Student{
Name: fmt.Sprintf("stu%d", i),
Age: rand.Intn(100),
Score: rand.Float32() * 100,
}
//因为参数是指针的指针,所以这里要传递指针的指针,
stu.next = *head1
*head1 = &stu
}
}
func main(){
var head *Student=new(Student)
head.Name="hua"
head.Age=18
head.Score=100
//因为这个函数要改变指针变量的值,所以要传递指针的地址进去
insertHead(&head)
trans(head)
}
理解:
如下:这里的insertHead中的head1是head的副本,开始head1和head是指向同一个内存地址,当head1=&stu的时候head1的地址,也就是head的副本的地址就变化了,但是head还是没有变化的。所以要改变指针的地址,也就是head的地址,这里函数必须要传递指针的指针才可以,在指针的基础之上多加一个*,
func insertHead(head1 **Student){}
然后传递的时候要传递指针的地址,如 insertHead(* head)
小结: 要改变指针变量的值,就要传递指针的指针进去
指针还有二级指针,三级指针等
删除链表
删除指定节点:
思路:
1、遍历,
2、遍历当前节点的上个节点的next等于当前节点的下一个节点,这个节点就删除了
3、如果第一次没有找到,那么就往后移动位置,即当前节点的上级节点等于当前节点,当前节点的下一个节点赋值给当前节点,
4、下面这个代码是有问题的,主要是这是一个副本。头部插入会有问题
代码:
package main
import (
"fmt"
"math/rand"
)
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(head **Student){
//从头插入
for i:=0;i<10;i++ {
stu := Student{
Name: fmt.Sprintf("stu%d", i),
Age: rand.Intn(100),
Score: rand.Float32() * 100,
}
//因为参数是指针的指针,所以这里要传递指针的指针,
stu.next = *head
*head = &stu
}
}
func delNode(p * Student){
//临时变量保存上一个节点
var prev *Student=p
/*
遍历链表
1、首先判断当前链表节点是否等于要删除的链表,如果是那么把当前链表节点上一个节点等于
当前链表节点的下一个节点
2、如果没有找到,那么当前链表节点就等于上个节点,当前链表节点就指向下个节点,也就是往后移动位置
*/
for p!=nil{
if p.Name=="stu6"{
prev.next=p.next
break
}
//如果没有找到,那么p就等于上个节点,p就指向下个节点
prev=p
p=p.next
}
}
func main(){
var head *Student=new(Student)
head.Name="hua"
head.Age=18
head.Score=100
insertHead(&head)
delNode(head)
trans(head)
}
怎么在上面stu6后面插入一个节点?
思路:
1、首先生成一个节点,让这个节点的下一个节点等于stu6的下一个节点
2、再让stu6的下一个节点指向插入的这个节点
package main
import (
"fmt"
"math/rand"
)
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(head **Student){
//从头插入
for i:=0;i<10;i++ {
stu := Student{
Name: fmt.Sprintf("stu%d", i),
Age: rand.Intn(100),
Score: rand.Float32() * 100,
}
//因为参数是指针的指针,所以这里要传递指针的指针,
stu.next = *head
*head = &stu
}
}
func delNode(p * Student){
//临时变量保存上一个节点
var prev *Student=p
/*
遍历链表
1、首先判断当前链表节点是否等于要删除的链表,如果是那么把当前链表节点上一个节点等于
当前链表节点的下一个节点
2、如果没有找到,那么当前链表节点就等于上个节点,当前链表节点就指向下个节点,也就是往后移动位置
*/
for p!=nil{
if p.Name=="stu6"{
prev.next=p.next
break
}
//如果没有找到,那么p就等于上个节点,p就指向下个节点
prev=p
p=p.next
}
}
//在stu5后面插入一个链表
func addNode(p *Student,newNode * Student){
for p!=nil{
if p.Name=="stu5"{
newNode.next=p.next
p.next=newNode
break
}
p=p.next
}
}
func main(){
var head *Student=new(Student)
head.Name="hua"
head.Age=18
head.Score=100
insertHead(&head)
delNode(head)
trans(head)
var newNode *Student=new(Student)
newNode.Name="stu1000"
newNode.Age=18
newNode.Score=100
addNode(head,newNode)
trans(head)
}
双向链表
定义 type Student struct{
Name sring
next * Student
prevn * Student
}
如果有两个指针分别指向前一个节点和后一个节点,我们叫做双链表
二叉树
定义:
type Student struct{
Name string
left * Student
right *Student
}
如果每个节点有两个指针分别用来指向左子树和右子树,我们把这样的结构叫做二叉树
对于二叉树,要用到广度优先或者深度优先的递归算法
下面是二叉树的类型图,下面的stu2的右边的孩子也可以是为nil,然后stu3如果没有孩子就叫做叶子节点,stu02是stu01的子树
代码:
下面采用递归的形式进行遍历二叉树,下面是深度优先的原理
如果要采取广度优先,那么每次遍历的时候就要把结果放到队列里面
前序遍历:是从根节点开始遍历的
package main import( "fmt" ) //声明二叉树 type Student struct{ Name string Age int Score float32 left *Student right *Student } func trans(root *Student){ if root==nil{ return } fmt.Println(root) //递归遍历左子树 trans(root.left) //递归然后遍历右子树 trans(root.right) } func main(){ //初始化root定点 var root *Student=new(Student) root.Name="stu01" root.Age=18 root.Score=100 root.left=nil //初始化 root.right=nil var left1 *Student=new(Student) left1.Name="stu02" left1.Age=19 left1.Score=100 //把这个节点插入到root的左边 root.left=left1 var right1 *Student=new(Student) right1.Name="stu04" right1.Age=19 right1.Score=100 //把这个节点插入到root的右边 root.right=right1 var left02 *Student=new(Student) left02.Name="stu03" left02.Age=18 left02.Score=100 //把这个节点插入到left1的左边 left1.left=left02 trans(root) } /* 下面结果分别是,Name Age Score 然后左边和右边的地址 &{stu01 18 100 0xc042082090 0xc0420820c0} &{stu02 19 100 0xc0420820f0 <nil>} &{stu03 18 100 <nil> <nil>} &{stu04 19 100 <nil> <nil>} */ 中序遍历:先遍历左子树,然后遍历根节点,然后遍历右节点 package main import( "fmt" ) //声明二叉树 type Student struct{ Name string Age int Score float32 left *Student right *Student } func trans(root *Student){ if root==nil{ return } //中序遍历 trans(root.left) fmt.Println(root) trans(root.right) /* 结果 &{stu03 18 100 <nil> <nil>} &{stu02 19 100 0xc0420820f0 <nil>} &{stu01 18 100 0xc042082090 0xc0420820c0} &{stu04 19 100 <nil> <nil>} */ } func main(){ //初始化root定点 var root *Student=new(Student) root.Name="stu01" root.Age=18 root.Score=100 root.left=nil //初始化 root.right=nil var left1 *Student=new(Student) left1.Name="stu02" left1.Age=19 left1.Score=100 //把这个节点插入到root的左边 root.left=left1 var right1 *Student=new(Student) right1.Name="stu04" right1.Age=19 right1.Score=100 //把这个节点插入到root的右边 root.right=right1 var left02 *Student=new(Student) left02.Name="stu03" left02.Age=18 left02.Score=100 //把这个节点插入到left1的左边 left1.left=left02 trans(root) } 后序遍历:首先遍历左子树,然后遍历右子树,最后遍历根节点 package main import( "fmt" ) //声明二叉树 type Student struct{ Name string Age int Score float32 left *Student right *Student } func trans(root *Student){ if root==nil{ return } //后序遍历 trans(root.left) trans(root.right) fmt.Println(root) /* 结果 &{stu03 18 100 <nil> <nil>} &{stu02 19 100 0xc04206e0f0 <nil>} &{stu04 19 100 <nil> <nil>} &{stu01 18 100 0xc04206e090 0xc04206e0c0} */ } func main(){ //初始化root定点 var root *Student=new(Student) root.Name="stu01" root.Age=18 root.Score=100 root.left=nil //初始化 root.right=nil var left1 *Student=new(Student) left1.Name="stu02" left1.Age=19 left1.Score=100 //把这个节点插入到root的左边 root.left=left1 var right1 *Student=new(Student) right1.Name="stu04" right1.Age=19 right1.Score=100 //把这个节点插入到root的右边 root.right=right1 var left02 *Student=new(Student) left02.Name="stu03" left02.Age=18 left02.Score=100 //把这个节点插入到left1的左边 left1.left=left02 trans(root) }