链表的尾部插入:先定义一个入口,接着批量插入。
单向链表,需要先定义一个入口,然后定义一个临时指针变量,存储这个入口的地址,利用for循环,指针变量作为节点临时存储的中间值,同时,指针变量的next指向下一个节点的地址。
多加了个%T查看类型,%p查看具体的地址,发现,程序会先把内存申请好,然后才开始工作。另外,内存结构确实是一个接着一个的。
for循环中,变量操作与地址操作 var stu = Student{} 也可以换成 stu := &Student ,直接操作地址。
package main
import (
"fmt"
"math/rand"
"time"
)
type Student struct{
Name string
Age int
Score float32
Next* Student
}
func trans( p *Student) {
for p.Next != nil{
fmt.Println(*p)
p = p.Next
time.Sleep(1*time.Second)
}
fmt.Println(*p)
}
func main() {
var head Student
var tail = &head
fmt.Printf("start: %T, %p\n",tail.Next,tail.Next)
for i:=0;i<10;i++{
var stu = Student {
Name: fmt.Sprintf("stu%d",i),
Age: rand.Intn(100),
Score: rand.Float32()*100,
}
tail.Next = &stu
fmt.Printf("body: %T,%p\n",tail,tail)
tail = &stu
}
fmt.Printf("end: %T,%p\n",tail.Next,tail.Next)
trans(&head)
}
运行结果:
PS F:\go\src\go_dev> .\main.exe
start: *main.Student, 0x0
body: *main.Student,0xc04207e210
body: *main.Student,0xc04207e240
body: *main.Student,0xc04207e270
body: *main.Student,0xc04207e2a0
body: *main.Student,0xc04207e2d0
body: *main.Student,0xc04207e300
body: *main.Student,0xc04207e330
body: *main.Student,0xc04207e360
body: *main.Student,0xc04207e390
body: *main.Student,0xc04207e3c0
end: *main.Student,0x0
{ 0 0 0xc04207e240}
{stu0 81 94.05091 0xc04207e270}
{stu1 47 43.77142 0xc04207e2a0}
{stu2 81 68.682304 0xc04207e2d0}
{stu3 25 15.651925 0xc04207e300}
{stu4 56 30.091187 0xc04207e330}
{stu5 94 81.36399 0xc04207e360}
{stu6 62 38.06572 0xc04207e390}
{stu7 28 46.888985 0xc04207e3c0}
{stu8 11 29.310184 0xc04207e3f0}
{stu9 37 21.855305 <nil>}
PS F:\go\src\go_dev>
头部插入:知道最后一个节点,新的节点的指针指最后一个,不断往前迭代。用变量的话,是值拷贝,用指针试试,指向地址不一样,就不会出现重复赋值。(最好先画图,涉及到指针时候,要分清改变的是指针还是指针的地址,这里是个隐患)
如果要改变变量指针的地址,就传入变量的地址
如果要改变指针指针的地址,就传入指针变量的地址
package main
import (
"fmt"
"math/rand"
"time"
)
type Student struct{
Name string
Age int
Score float32
Next* Student
}
func trans( p *Student) {
for p.Next != nil{
fmt.Println(*p)
p = p.Next
time.Sleep(1*time.Second)
}
fmt.Println(*p)
}
func insertHead(p **Student) {
for i := 0;i<10; i++ {
var stu = Student{
Name: fmt.Sprintf("stu%d",i),
Age: rand.Intn(100),
Score: rand.Float32()*100,
}
stu.Next = *p
//p = &stu 调用指针的副本,这里是个坑
*p = &stu
}
}
func main() {
fmt.Println("insert head")
var tail *Student = new(Student)
/*
for i := 0;i<10; i++ {
var stu = Student{
Name: fmt.Sprintf("stu%d",i),
Age: rand.Intn(100),
Score: rand.Float32()*100,
}
stu.Next = tail
tail = &stu
}
*/
insertHead(&tail)
trans(tail)
}
运行结果:
PS F:\go\src\go_dev> .\main.exe
insert head
{stu9 85 30.152267 0xc0420726c0}
{stu8 37 5.912065 0xc042072690}
{stu7 29 7.9453626 0xc042072660}
{stu6 87 60.72534 0xc042072630}
{stu5 41 2.8303082 0xc042072600}
{stu4 90 69.67192 0xc0420725d0}
{stu3 87 20.658266 0xc0420725a0}
{stu2 47 29.708258 0xc042072570}
{stu1 28 86.249146 0xc042072540}
{stu0 95 36.08714 0xc042072510}
{ 0 0 <nil>}
PS F:\go\src\go_dev>
解析一下:这个例子很经典:
//需要改变指针指向的地址,因为函数的参数是一个值拷贝,也就是副本,指针也是一个副本,形参改变,不会改变外面的实参。 也就是说,只有用传地址进去,才能改变外面实参的值。
func insertHead(p **Student) {
for i := 0;i<10; i++ {
var stu = Student{
Name: fmt.Sprintf("stu%d",i),
Age: rand.Intn(100),
Score: rand.Float32()*100,
}
stu.Next = *p
//p = &stu 调用指针的副本,这里是个坑,如果不传入指针的地址,那么改变的只是副本的值。
*p = &stu
}
}
//这里需要将指针的地址传进去
func main() {
fmt.Println("insert head")
var tail *Student = new(Student)
insertHead(&tail)
trans(tail)
}
图解来看是这样的: head是指针,func传入指针的值,那就会生成一个副本,变成head1,修改head1的值,不会影响head指向的地址,如果要改变head,就需要用指针的指针,传入指针的地址,修改head1,才能改变head的值。
2019年12月12日 今天天气特别好。
昨晚加湿器没水,睡到后半夜太干燥,今天喉咙出问题了,有点难受。
今天拼多多的活动,通过小伙伴的努力,攒到了200元,真棒~ 和大家平分,结果小伙伴们有的没拿,改天弥补他们。
嗯,双十二了,买条内存,升级一下电脑。
菲律宾出差的事情有消息了,可能月底就要出发,有点不舍,因为刚刚熟悉一个地方,又要离开。这次出差结束,回来应该会常驻这边,也挺好的,小伙伴说,明年春暖花开的时候,一起去爬山~