go与链表

1. 链表

它的存储特点为:可以用任意一组存储单元来存储单链表中的数据元素(存储单元可以是不连续的),而且,除了存储每个数据元素ai外,还必须存储指示其直接后继元素的信息。这两部分信息组成的数据元素ai的存储映像称为结点。N个结点链在一块被称为链表,当结点只包含其后继结点信息的链表就被称为单链表,而链表的第一个结点通常被称为头结点,对于单链表,又可以将其分为有头结点的单链表无头结点的单链表.当节点既包含其后继节点信息,也包含前驱节点信息的链表被称为双链表

2. 单链表

2.1 带头结点的单链表

在单链表的开始节点之前附设一个类型相同的节点,称为头结点。头结点的数据域可以不存储任何信息(也可以存放如线性表的长度等附加信息),头结点的指针域存储指向开始节点的指针(即第一个元素节点的存储位置):

优点:

  • 对于带头结点的链表,当在链表的任何结点之前插入新结点或删除链表中任何结点时,所要做的都是修改前一个结点的指针域,因为任何结点都有前驱结点。若链表没有头结点,则首元素结点没有前驱结点,在其前面插入结点或删除该结点时操作会复杂些,需要进行特殊的处理。

  • 对于带头结点的链表,链表的头指针是指向头结点的非空指针,因此,对空链表与非空链表的处理是一样的。
    在这里插入图片描述

2.2 不带头结点的单链表

单链表的开始节点之前没有附设一个类型相同的空节点。直接从开始节点开始,随着在单链表的开始节点直接插入新的节点,头结点会随时变化(此处的头结点是指谁是第一个节点即为头结点)
在这里插入图片描述

3. 双链表

对于一个节点,有些和单链表一样有存储数据的data,指向后方的next(指针)。它拥有单链表的所有操作和内容。但是他还有一个前驱节点pre(指针)

在这里插入图片描述

对于双向量表,给定任意一个节点,都可以遍历整个链表的数据。相比于单链表,进行数据查找快,但是空间开销是单链表的两倍,相当于是空间换时间。需要根据具体的实际情况进行选择

4. go语言与链表

4.1 创建单链表

package main

import "fmt"

//链表定义
type LNode struct {
	Data interface{}
	Next *LNode
}

func main() {
	head := &LNode{}          // 定义头结点
	cur := head               // 移动变量
	for i := 1; i < 10; i++ { // 创建长度为10的链表
		cur.Next = &LNode{} // 指向链表的后驱节点
		cur.Next.Data = i   // 指向后驱节点的数据域
		cur = cur.Next      // 指针向后移动一位
	}
	printNodeList(head)
}

func printNodeList(head *LNode) {
	for cur := head.Next; cur != nil; cur = cur.Next {
		fmt.Printf("%v ", cur.Data)
	}
}

4.2 创建双向链表

package main

import (
	"fmt"
	"strconv"
	"sync"
)

type Data struct {
	Name string `json:"name"`
	Age  int    `json:"age"`
}

type Node struct {
	Pre  *Node
	Next *Node
	Data Data
}

type NodeList struct {
	mutex *sync.Mutex
	Head  *Node
	Tail  *Node
	Size  uint
}

type NodeInterface interface {
	Init()
	Append(node *Node)
	InsertAfterNode(node *Node, data Data) error
	InsertIndex(index uint, data Data) error
	DelNode(node *Node)
	PrintNodeList()
	IsEmpty() bool
	GetNodeListLen() uint
	Exist(node *Node) bool
	InsertHead(data Data)
}

func Factory() NodeInterface {
	return &NodeList{}
}

// Init,链表的初始化
func (this *NodeList) Init() {
	this.Head = nil
	this.Tail = nil
	this.Size = 0
	this.mutex = new(sync.Mutex)
}

// Append,在链表尾部插入节点
func (this *NodeList) Append(node *Node) {
	this.mutex.Lock()
	defer this.mutex.Unlock()
	if this.Size == 0 {
		this.Head = node
		this.Tail = node
		this.Size = 1
		return
	}
	nodePre := this.Tail
	this.Tail.Next = node
	node.Pre = nodePre
	this.Tail = node
	this.Size++
}

// Exit, 判断节点是否在当前链表中存在
func (this *NodeList) Exist(node *Node) bool {
	var p = this.Head
	for p != nil {
		if p == node {
			return true
		}
		p = p.Next
	}
	return false
}

// InsertAfterNode,在指定节点后插入数据
func (this *NodeList) InsertAfterNode(node *Node, data Data) error {
	// 如果当前节点存在插入数据,不存在报错
	if !this.Exist(node) {
		return fmt.Errorf("the current node is not exist")
	}
	newNode := &Node{
		Data: data,
		Next: nil,
	}
	if node.Next == nil {
		this.Append(newNode)
	} else if node.Pre == nil {
		newNode.Next = node.Next
		newNode.Pre = node
		node.Next = newNode
		this.Size++
	} else {
		newNode.Next = node.Next
		newNode.Pre = node
		node.Next = newNode
		this.Size++
	}
	return nil
}

// InsertIndex,在指定下标处插入数据
func (this *NodeList) InsertIndex(index uint, data Data) error {
	// 判断下标值是否合法
	if index > this.Size {
		return fmt.Errorf("the index is more than the nodeList subscript")
	}

	// index=0,即在链表头部插入数据
	if index == 0 {
		this.InsertHead(data)
		return nil
	}

	// 遍历到指定下标对应的节点
	var p = this.Head
	for i := 0; i < int(index); i++ {
		p = p.Next
	}

	if err := this.InsertAfterNode(p, data); err != nil {
		return err
	}

	return nil
}

// InsertHead,在链表头部插入数据
func (this *NodeList) InsertHead(data Data) {
	newNode := &Node{
		Data: data,
		Next: this.Head,
	}
	this.Head.Pre = newNode
	this.Head = newNode
	if this.Size == 0 {
		this.Tail = newNode
	}
	this.Size++
}

// DelNode,删除指定节点
func (this *NodeList) DelNode(node *Node) {
	// 判断是否是头节点
	if this.Head == node {
		this.Head = this.Head.Next
	}

	// 判断是否是尾节点
	if this.Tail == node {
		p := this.Head
		for p.Next != node {
			p = p.Next
		}
		p.Next = nil
		p.Next.Pre = nil
	}

	// 如果是中间节点
	p := this.Head
	for p.Next != node {
		p = p.Next
	}
	p.Next = node.Next
	node.Next.Pre = node.Pre
	this.Size--
}

// GetNodeListLen,获取链表长度
func (this *NodeList) GetNodeListLen() uint {
	return this.Size
}

// IsEmpty,判断链表是否为空
func (this *NodeList) IsEmpty() bool {
	return this.Size == 0
}

// PrintNodeList, 打印链表信息
func (this *NodeList) PrintNodeList() {
	fmt.Println("==============打印链表信息==============")
	if this.IsEmpty() {
		fmt.Println("当前链表为空")
	}
	fmt.Printf("链表长度 : %v\n", this.GetNodeListLen())
	p := this.Head
	//for p.Next != nil {
	for {
		fmt.Printf("the node : %v\n", p.Data)
		if p.Next == nil {
			break
		} else {
			p = p.Next
		}
	}
	fmt.Println("==============打印链表信息==============")
}

func main() {
	factory := Factory()
	factory.Init()

	for i := 0; i <= 10; i++ {
		factory.Append(&Node{
			Data: Data{
				Name: "test_" + strconv.Itoa(i),
				Age:  i,
			},
			Next: nil,
		})
	}
	fmt.Println("=============初始化的链表=============")
	factory.PrintNodeList()
	fmt.Println("=============初始化的链表=============")

	fmt.Println("")
	fmt.Println("")
	fmt.Println("")
	fmt.Println("=============在指定节点后插入数据=============")
	data := Data{
		Name: "zhangyang",
		Age:  28,
	}
	node := factory.(*NodeList).Head
	for i := 0; i < 6; i++ {
		node = node.Next
	}
	if err := factory.InsertAfterNode(node, data); err != nil {
		panic(err)
	}
	if err := factory.InsertAfterNode(node.Next, data); err != nil {
		panic(err)
	}
	factory.PrintNodeList()
	fmt.Println("=============在指定节点后插入数据=============")
	fmt.Println("")
	fmt.Println("")
	fmt.Println("")
	fmt.Println("=============在指定下标插入数据=============")
	data = Data{
		Name: "taoyingying",
		Age:  25,
	}
	if err := factory.InsertIndex(8, data); err != nil {
		panic(err)
	}
	factory.PrintNodeList()
	fmt.Println("=============在指定下标插入数据=============")
	fmt.Println("")
	fmt.Println("")
	fmt.Println("")
	fmt.Println("=============删除指定节点=============")
	node = factory.(*NodeList).Head
	for i := 0; i < 7; i++ {
		node = node.Next
	}
	factory.DelNode(node)
	factory.PrintNodeList()
	fmt.Println("=============删除指定节点=============")
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值