Go 结构体链表

2024-07-17 16:25:01 浏览数 (1)

Go 结构体链表

链表结构

1. 定义

链表是一种线性数据结构,由一系列节点组成,每个节点包含两个部分:数据部分和指向下一个节点的指针(或引用)部分。链表的第一个节点称为头节点(head),最后一个节点的指针指向空(null)。

2. 类型
  • 单链表:每个节点指向下一个节点。
  • 双链表:每个节点有两个指针,分别指向前一个节点和后一个节点。
  • 循环链表:最后一个节点指向头节点,形成一个环。

链表的优缺点

优点
  1. 动态内存分配:链表不需要预先分配固定大小的内存,节点可以在需要时动态创建,内存利用率更高。
  2. 插入和删除操作高效:在链表中插入和删除节点的操作只需要改变指针,时间复杂度为 O(1)。相比之下,数组需要移动元素,时间复杂度为 O(n)。
  3. 灵活性:链表的大小可以根据需要动态变化,非常适合需要频繁插入和删除操作的场景。
缺点
  1. 随机访问效率低:由于链表不支持下标访问,要访问某个节点只能从头节点开始逐个遍历,时间复杂度为 O(n)。
  2. 额外的内存开销:每个节点除了存储数据外,还需要存储指向下一个节点的指针,这会增加额外的内存消耗。
  3. 缓存性能差:链表节点在内存中通常是离散存储的,缺乏局部性,会导致缓存命中率较低,影响访问性能。

使用场景

  1. 需要频繁插入和删除的场景:例如实现队列、栈等数据结构。
  2. 不确定数据大小的场景:例如动态集合。
  3. 内存管理需要灵活的场景:例如实现链表分配器(linked list allocator)。

单项链表的基本操作

image-20240713181145134image-20240713181145134

使用 struct 定义单链表

假设我们有一个结构体 Student

代码语言:javascript复制
 type Student struct {
     Name  string
     Age   int
     Score float64
 }
1.使用变量声明结构体

直接声明一个结构体变量:

代码语言:javascript复制
 var stu Student

这种方式 stu 是一个 Student 类型的变量,不是指针。

2. 使用 new 函数创建结构体指针
代码语言:javascript复制
 var stu *Student = new(Student)

这种方式 stu 是一个指向 Student 类型的指针,new 函数会分配内存并返回指向该类型的新零值指针。

3. 使用取地址符 & 创建结构体指针
代码语言:javascript复制
 var stu *Student = &Student{}

这种方式 stu 是一个指向 Student 类型的指针,通过 &Student{} 创建一个 Student 实例并返回其地址。

访问结构体字段

无论是通过结构体变量还是指针,我们都可以访问结构体的字段。

1. 直接通过结构体变量访问字段
代码语言:javascript复制
 var stu Student
 stu.Name = "John"
 stu.Age = 20
 stu.Score = 90.5
2. 通过结构体指针访问字段

使用指针访问字段有两种方式:

  • 使用简便的点运算符:
代码语言:javascript复制
 var stu *Student = new(Student)
 stu.Name = "John"    // 等同于 (*stu).Name
 stu.Age = 20         // 等同于 (*stu).Age
 stu.Score = 90.5     // 等同于 (*stu).Score
  • 显式解引用:
代码语言:javascript复制
 (*stu).Name = "John"
 (*stu).Age = 20
 (*stu).Score = 90.5

定义一个单项链表

next 是指针类型的属性,指向 Student struct 类型数据,也就是下一个节点的数据类型

代码语言:javascript复制
 type Student struct {
     Name  string
     Age   int
     Score float32
     next  *Student
 }

完整代码

代码语言:javascript复制
type Student struct {
	Name  string
	Age   int
	Score float32
	next  *Student		//存放下一个结构体的地址,用*直接指向下一个结构体
}

func main() {
	//头部结构体
	var head Student
	head.Name = "王五"
	head.Age = 20
	head.Score = 78

	//第二个结构体节点
	var stu1 Student
	stu1.Name = "小张"
	stu1.Age = 2
	stu1.Score = 1

	head.next = &stu1

	//第三个结构体节点
	var stu2 Student
	stu2.Name = "王五"
	stu2.Age = 18
	stu2.Score = 60

	stu1.next = &stu2

	Req(&head)
}

func Req(tmp *Student) {		//tmp指针是指向下一个结构体的地址,加*就是下一个结构体
	for tmp != nil {			//遍历输出链表中每个结构体,判断是否为空
		fmt.Println(*tmp)
		tmp = tmp.next			//tmp变更为下一个结构体地址
	}
}

尾部添加节点

代码语言:javascript复制
package main

import (
	"fmt"
	"math/rand"
)

type Student struct {
	Name  string
	Age   int
	Score float32
	next  *Student
}

// AppendNode adds a new student to the end of the linked list
func AppendNode(head *Student, newNode *Student) {
	// Traverse to the end of the list
	current := head
	for current.next != nil {
		current = current.next
	}
	// Add the new node at the end
	current.next = newNode
}

func main() {
	// Initialize head node
	var head Student
	head.Name = "head"
	head.Age = 28
	head.Score = 88

	// Initialize subsequent nodes and append them to the list
	nodes := []Student{
		{Name: "stu1", Age: 25, Score: 100},
		{Name: "stu2", Age: 18, Score: 60},
		{Name: "stu3", Age: 18, Score: 80},
	}

	for _, node := range nodes {
		AppendNode(&head, &node)
	}

	// Dynamically create and append more nodes
	for i := 4; i < 10; i   {
		stu := &Student{
			Name:  fmt.Sprintf("stu%d", i),
			Age:   rand.Intn(100),
			Score: rand.Float32() * 100,
		}
		AppendNode(&head, stu)
	}

	// Print the linked list
	Req(&head)
}

// Req traverses and prints the linked list
func Req(tmp *Student) {
	for tmp != nil {
		fmt.Println(*tmp)
		tmp = tmp.next
	}
}

头部插入节点

代码语言:javascript复制
package main

import (
	"fmt"
	"math/rand"
)

type Student struct {
	Name  string
	Age   int
	Score float32
	next  *Student
}

// PrependNode adds a new student to the beginning of the linked list
func PrependNode(head **Student, newNode *Student) {
	newNode.next = *head
	*head = newNode
}

func main() {
	// Initialize head node
	head := &Student{
		Name:  "head",
		Age:   28,
		Score: 88,
	}

	// Initialize subsequent nodes and prepend them to the list
	nodes := []Student{
		{Name: "stu1", Age: 25, Score: 100},
		{Name: "stu2", Age: 18, Score: 60},
		{Name: "stu3", Age: 18, Score: 80},
	}

	for _, node := range nodes {
		newNode := node // Make a copy to avoid pointer issues
		PrependNode(&head, &newNode)
	}

	// Dynamically create and prepend more nodes
	for i := 4; i < 10; i   {
		stu := &Student{
			Name:  fmt.Sprintf("stu%d", i),
			Age:   rand.Intn(100),
			Score: rand.Float32() * 100,
		}
		PrependNode(&head, stu)
	}

	// Print the linked list
	Req(head)
}

// Req traverses and prints the linked list
func Req(tmp *Student) {
	for tmp != nil {
		fmt.Println(*tmp)
		tmp = tmp.next
	}
}

指定节点后添加新节点

代码语言:javascript复制
package main

import (
	"fmt"
	"math/rand"
)

type Student struct {
	Name  string
	Age   int
	Score float32
	next  *Student
}

// InsertNode inserts a new student after the specified node in the linked list
func InsertNode(head *Student, targetName string, newNode *Student) {
	current := head
	for current != nil {
		if current.Name == targetName {
			newNode.next = current.next
			current.next = newNode
			return
		}
		current = current.next
	}
	fmt.Printf("Node with name %s not foundn", targetName)
}

func main() {
	// Initialize head node
	head := &Student{
		Name:  "head",
		Age:   28,
		Score: 88,
	}

	// Initialize subsequent nodes and append them to the list
	nodes := []Student{
		{Name: "stu1", Age: 25, Score: 100},
		{Name: "stu2", Age: 18, Score: 60},
		{Name: "stu3", Age: 18, Score: 80},
	}

	// Append nodes to the list
	current := head
	for i := range nodes {
		current.next = &nodes[i]
		current = &nodes[i]
	}

	// Dynamically create and append more nodes
	for i := 4; i < 6; i   {
		stu := &Student{
			Name:  fmt.Sprintf("stu%d", i),
			Age:   rand.Intn(100),
			Score: rand.Float32() * 100,
		}
		current.next = stu
		current = stu
	}

	// Insert a new node after "stu2"
	newNode := &Student{
		Name:  "newStu",
		Age:   22,
		Score: 75,
	}
	InsertNode(head, "stu2", newNode)

	// Print the linked list
	Req(head)
}

// Req traverses and prints the linked list
func Req(tmp *Student) {
	for tmp != nil {
		fmt.Println(*tmp)
		tmp = tmp.next
	}
}

删除节点

代码语言:javascript复制
package main

import (
	"fmt"
	"math/rand"
)

type Student struct {
	Name  string
	Age   int
	Score float32
	next  *Student
}

// DeleteNode removes the student with the specified name from the linked list
func DeleteNode(head **Student, targetName string) {
	current := *head
	var prev *Student = nil

	// If head needs to be removed
	if current != nil && current.Name == targetName {
		*head = current.next
		return
	}

	// Search for the target node to delete
	for current != nil && current.Name != targetName {
		prev = current
		current = current.next
	}

	// If the target node was not found
	if current == nil {
		fmt.Printf("Node with name %s not foundn", targetName)
		return
	}

	// Remove the target node
	prev.next = current.next
}

func main() {
	// Initialize head node
	head := &Student{
		Name:  "head",
		Age:   28,
		Score: 88,
	}

	// Initialize subsequent nodes and append them to the list
	nodes := []Student{
		{Name: "stu1", Age: 25, Score: 100},
		{Name: "stu2", Age: 18, Score: 60},
		{Name: "stu3", Age: 18, Score: 80},
	}

	// Append nodes to the list
	current := head
	for i := range nodes {
		current.next = &nodes[i]
		current = &nodes[i]
	}

	// Dynamically create and append more nodes
	for i := 4; i < 6; i   {
		stu := &Student{
			Name:  fmt.Sprintf("stu%d", i),
			Age:   rand.Intn(100),
			Score: rand.Float32() * 100,
		}
		current.next = stu
		current = stu
	}

	// Delete a node with name "stu2"
	DeleteNode(&head, "stu2")

	// Print the linked list
	Req(head)
}

// Req traverses and prints the linked list
func Req(tmp *Student) {
	for tmp != nil {
		fmt.Println(*tmp)
		tmp = tmp.next
	}
}
go

0 人点赞