详解Go语言中单链表的使用

目录
  • 链表
  • 单链表结构
    • 创建节点
    • 遍历链表
    • 头插法
    • 尾插法
    • 遍历方法
    • 链表长度
    • 链表转数组
    • 数组转链表

链表

一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。使用链表结构可以避免在使用数组时需要预先知道数据大小的缺点,链表结构可以充分利用计算机内存空间,实现灵活的内存动态管理。但是链表失去了数组随机读取的优点,同时链表由于增加了结点的指针域,空间开销比较大。

单链表结构

利用 struct 可以包容多种数据类型,结构体内也可以包含多个成员,这些成员可以是基本类型、自定义类型、数组类型,也可以是指针类型。这里可以使用指针类型成员来存放下一个结点的地址。如以下定义,成员 data 用来存放结点中的数据(整数类型),next 是指针类型的成员,它指向 ListNode struct 类型数据,也就是下一个结点的数据类型。

type ListNode struct {
    data int
    next *ListNode
}

创建节点

节点声明和赋值有以下几种格式:

package main

import "fmt"

type ListNode struct {
    data int
    next *ListNode
}

func main() {

    var head *ListNode
    head = new(ListNode)
    head.data = 1

    var node1 = new(ListNode)
    node1.data = 2

    var node2 = &ListNode{3, nil}

    var node3 = &ListNode{data: 4}

    fmt.Println(*head)
    fmt.Println(*node1)
    fmt.Println(*node2)
    fmt.Println(*node3)

}

/* 输出:
{1 <nil>}
{2 <nil>}
{3 <nil>}
{4 <nil>}
*/

遍历链表

一个for循环即可,结构描述的链表没有空链表的,不论data是何种类型,一旦声明即使不马上赋值也会有类型默认值,比如new(ListNode)即赋值了ListNode{0, nil}。

func showNode(p *ListNode) {
    fmt.Print(*p)
    for p.next != nil {
        p = p.next
        fmt.Print("->", *p)
    }
    fmt.Println()
}

头插法

新结点放在链表的最前面

package main

import "fmt"

type ListNode struct {
    data int
    next *ListNode
}

func showNode(p *ListNode) {
    fmt.Print(*p)
    for p.next != nil {
        p = p.next
        fmt.Print("->", *p)
    }
    fmt.Println()
}

func main() {
    var head = &ListNode{0, nil}

    for i := 1; i < 5; i++ {
        var node = ListNode{data: i}
        node.next = head
        head = &node
    }

    showNode(head)

}

/* 输出:
{4 0xc000084250}->{3 0xc000084240}->{2 0xc000084230}->{1 0xc000084220}->{0 <nil>}
*/

尾插法

新结点追加到链表的最后面

package main

import "fmt"

type ListNode struct {
    data int
    next *ListNode
}

func showNode(p *ListNode) {
    fmt.Print(*p)
    for p.next != nil {
        p = p.next
        fmt.Print("->", *p)
    }
    fmt.Println()
}

func main() {

    var head, tail *ListNode
    head = &ListNode{0, nil}
    tail = head
    for i := 1; i < 5; i++ {
        var node = ListNode{data: i}
        (*tail).next = &node
        tail = &node
    }

    showNode(head)

}

/* 输出:
{0 0xc000084220}->{1 0xc000084230}->{2 0xc000084240}->{3 0xc000084250}->{4 <nil>}
*/

遍历方法

方法的定义:参数表放在函数名前

package main

import "fmt"

type ListNode struct {
    data int
    next *ListNode
}

func (p *ListNode) travel() {
    fmt.Print(p.data)
    for p.next != nil {
        p = p.next
        fmt.Print("->", p.data)
    }
    fmt.Println("<nil>")
}

func main() {

    var head = &ListNode{0, nil}
    head.travel()

    for i := 1; i < 10; i++ {
        var node = ListNode{data: i}
        node.next = head
        head = &node
    }

    head.travel()

    var root *ListNode
    root = new(ListNode)
    root.travel()

}

/* 输出:
0<nil>
9->8->7->6->5->4->3->2->1->0<nil>
0<nil>
*/

链表长度

注意:函数与方法的区别

package main

import "fmt"

type ListNode struct {
    data int
    next *ListNode
}

func (head *ListNode) size() int {
    size := 1
    for head = head.next; head != nil; size++ {
        head = head.next
    }
    return size
}

func Len(head *ListNode) int {
    size := 1
    for head = head.next; head != nil; size++ {
        head = head.next
    }
    return size
}

func main() {

    var head = &ListNode{0, nil}
    fmt.Println(Len(head))
    fmt.Println(head.size())

    for i := 1; i < 10; i++ {
        var node = ListNode{data: i}
        node.next = head
        head = &node
    }

    fmt.Println(Len(head))
    fmt.Println(head.size())

}

/* 输出:
1
1
10
10
*/

链表转数组

package main

import (
    "fmt"
)

type ListNode struct {
    data int
    next *ListNode
}

func (head *ListNode) size() int {
    size := 1
    for head = head.next; head != nil; size++ {
        head = head.next
    }
    return size
}

func (head *ListNode) tolist() []int {
    var res []int
    res = make([]int, 0, head.size())
    for head.next != nil {
        res = append(res, head.data)
        head = head.next
    }
    res = append(res, head.data)
    return res
}

func (head *ListNode) tolist2() []int {
    var res []int
    res = make([]int, 0, head.size())
    res = append(res, head.data)
    head = head.next
    for head != nil {
        res = append(res, head.data)
        head = head.next
    }
    return res
}

func main() {

    var head = &ListNode{0, nil}

    for i := 1; i < 10; i++ {
        var node = ListNode{data: i}
        node.next = head
        head = &node
    }

    fmt.Println(head.tolist())

    var root, tail *ListNode
    root = &ListNode{0, nil}
    tail = root
    for i := 1; i < 10; i++ {
        var node = ListNode{data: i}
        (*tail).next = &node
        tail = &node
    }

    fmt.Println(root.tolist2())

}

/* 输出:
[9 8 7 6 5 4 3 2 1 0]
[0 1 2 3 4 5 6 7 8 9]
*/

数组转链表

package main

import "fmt"

type ListNode struct {
    data int
    next *ListNode
}

func (p *ListNode) travel() {
    fmt.Print(p.data)
    for p.next != nil {
        p = p.next
        fmt.Print("->", p.data)
    }
    fmt.Println("<nil>")
}

func toNode(list []int) *ListNode {
    var head, tail *ListNode
    head = &ListNode{list[0], nil}
    tail = head
    for i := 1; i < len(list); i++ {
        var node = ListNode{data: list[i]}
        (*tail).next = &node
        tail = &node
    }
    return head
}

func main() {

    var lst = []int{1, 3, 2, 3, 5, 6, 6, 8, 9}
    toNode(lst).travel()

}

/* 输出:
1->3->2->3->5->6->6->8->9<nil>
*/

到此这篇关于详解Go语言中单链表的使用的文章就介绍到这了,更多相关Go语言单链表内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 详解go语言单链表及其常用方法的实现

    目的 在刷算法题中经常遇到关于链表的操作,在使用go语言去操作链表时不熟悉其实现原理,目的是为了重温链表这一基础且关键的数据结构. 1.链表的特点和初始化 1.1.链表的特点 用一组任意的存储单元存储线性表的数据元素(这组存储单元可以是连续的,也可以是不连续的) 1.2.结点 结点(node) 数据域 => 存储元素信息 指针域 => 存储结点的直接后继,也称作指针或链 首元结点 是指链表中存储的第一个数据元素的结点 头结点 是在首元结点之前附设的一个结点,其指针域指向首元结点(非必须) 头指

  • Go语言单链表实现方法

    本文实例讲述了Go语言单链表实现方法.分享给大家供大家参考.具体如下: 1. singlechain.go代码如下: 复制代码 代码如下: ////////// //单链表 -- 线性表 package singlechain //定义节点 type Node struct {     Data int     Next *Node } /* * 返回第一个节点 * h 头结点  */ func GetFirst(h *Node) *Node {     if h.Next == nil {  

  • Go语言利用接口实现链表插入功能详解

    目录 1. 接口定义 1.1 空接口 1.2 实现单一接口 1.3 接口多方法实现 2. 多态 2.1 为不同数据类型的实体提供统一的接口 2.2 多接口的实现 3. 系统接口调用 4. 接口嵌套 5. 类型断言 5.1 断言判断 5.2 多类型判断 6. 使用接口实现链表插入 1. 接口定义 Interface 类型可以定义一组方法,不需要实现,并且不能包含任何的变量,称之为接口 接口不需要显示的实现,只需要一个变量,含有接口类型中的所有方法,那么这个变量就实现了这个接口,如果一个变量含有多个

  • Go 语言结构体链表的基本操作

    目录 1. 什么是链表 2. 单项链表的基本操作 3. 使用 struct 定义单链表 4. 尾部添加节点方法一 5. 头部插入节点方法一 6. 指定节点后添加新节点 7. 删除节点 1. 什么是链表 链表是一种物理存储单元上非连续.非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的. 链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成.每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域. 使用链表结构可以避免在使用数组

  • Go语言学习之链表的使用详解

    目录 1. 什么是链表 2. 单项链表的基本操作 3. 使用 struct 定义单链表 4. 尾部添加节点 5. 头部插入节点 6. 指定节点后添加新节点 7. 删除节点 1. 什么是链表 链表是一种物理存储单元上非连续.非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的. 链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成.每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域. 使用链表结构可以避免在使用数组时需要预先知

  • 详解Go语言中单链表的使用

    目录 链表 单链表结构 创建节点 遍历链表 头插法 尾插法 遍历方法 链表长度 链表转数组 数组转链表 链表 一种物理存储单元上非连续.非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的.链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成.每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域.使用链表结构可以避免在使用数组时需要预先知道数据大小的缺点,链表结构可以充分利用计算机内存空间,实现灵活的内存动态管理.但是链表失去

  • C语言中单链表(不带头结点)基本操作的实现详解

    目录 一.单链表的概念 二.单链表的基本操作 1.创建单个结点 2.创建具有n个结点的链表 3.打印单链表 4.尾插 5.尾删 6.头插 7.头删 8.查找某个结点 9.在某个结点后面插入 10.在某个结点前面插入 11.删除某个位置后面的结点 12.删除某个结点 13.销毁单链表 三.测试代码 通过对顺序表的学习,我们可以发现顺序表有以下几点缺陷: 1.空间不够时需要扩容,扩容尤其是用realloc进行异地扩容时,是有一定代价的,其次还可能存在一定空间浪费. 2.头部或者中间插入删除,需要挪动

  • 详解C语言中二级指针与链表的应用

    目录 前言 二级指针讲解 链表的应用 定义双链表的结构体 创建双链表 前言 这篇文章即将解决你看不懂或者不会写链表的基本操作的问题,对于初学者而言,有很多地方肯定是费解的.比如函数的参数列表的多样化,动态分配内存空间函数malloc等,其实这些知识和指针联系紧密,尤其是二级指针.那么开始好好的学习这篇文章吧! 二级指针讲解 简述:其实就是一个指针指向另一个指针的地址. 我们都知道指针指向地址,但是指针自身也是一个变量,当然也可以被二级指针所指向. 语法:形如 int x = 10; int *q

  • 详解C语言内核中的链表与结构体

    Windows内核中是无法使用vector容器等数据结构的,当我们需要保存一个结构体数组时,就需要使用内核中提供的专用链表结构LIST_ENTRY通过一些列链表操作函数对结构体进行装入弹出等操作,如下代码是本人总结的内核中使用链表存储多个结构体的通用案例. 首先实现一个枚举用户进程功能,将枚举到的进程存储到链表结构体内. #include <ntifs.h> #include <windef.h> extern PVOID PsGetProcessPeb(_In_ PEPROCES

  • 详解C语言进程同步机制

    本文是对进程同步机制的一个大总结(9000+字吐血总结),涵盖面非常的全,包括了进程同步的一些概念.软件同步机制.硬件同步机制.信号量机制和管程机制,对每种机制结合代码做了详细的介绍,并且对琐碎的知识点和概念解释的非常清晰. ​ 在前面的博客中讲述了进程的状态及其状态的转换,每种状态的含义和转换的原因.同样我们也知道,在OS引入了进程后,可以使系统中的多道程序可以并发的执行,进程的并发执行一方面极大的提高了系统的资源利用率和吞吐量,但是另一方面却使系统变得更加复杂,如果不能采取有效的措施,对多个

  • 详解C语言中双向循环链表的实现

    目录 实现细节 辅助理解图 具体实现代码 1.对链表进行初始化 2.任意位置前的插入 3.任意位置的删除 4.头插和尾删 完整代码 头文件 具体函数 测试 实现细节 1.带一个哨兵位(哨兵节点,初始节点,不存储有效数据,用来方便后期数据的存储与查找) 2.与单向链表不同的是,双向链表中每个数据节点包含两个指针,分别指向前后两个节点 3.双向链表是循环的,其尾节点后不是空指针,而是与头部的哨兵节点通过指针相连 辅助理解图 具体实现代码 1.对链表进行初始化 初始化:哨兵位的前后指针均指向哨兵节点本

  • 详解C语言中结构体的使用

    目录 结构体的声明 结构体成员的类型 结构体成员的访问 结构体的声明 结构体的定义:结构体是一些值的集合,这些值称为成员变量,结构体的每个成员可以是不同类型的变量. 举例: //定义结构体类型 struct tag//struct结构体关键字 tag结构体标签 struct tag结构体类型 { //成员变量 char name[20]; short age; char telphone[12]; char sex[5]; }s1,s2,s3;//s1,s2,s3是三个全局结构体变量 int m

  • 详解C语言如何实现双向带头循环链表

    目录 一.双向循环链表与顺序表的区别 二.List.h 三.List.c 1.带头双向循环链表的初始化 2.带头双向循环链表的销毁 3.带头双向循环链表的打印 4.动态开辟一个节点 5.带头双向循环链表的判空 6.带头双向循环链表的尾插.尾删 7.带头双向循环链表的头插.头删 8.带头双向循环链表的长度 9.带头双向循环链表的查找.任意位置插入.删除 一.双向循环链表与顺序表的区别 不同点 顺序表 双向带头循环链表 在内存中的存储方式 连续存储 不一定连续 随机访问 支持随机访问 不支持随访问

  • 详解C语言内核中的自旋锁结构

    提到自旋锁那就必须要说链表,在上一篇<驱动开发:内核中的链表与结构体>文章中简单实用链表结构来存储进程信息列表,相信读者应该已经理解了内核链表的基本使用,本篇文章将讲解自旋锁的简单应用,自旋锁是为了解决内核链表读写时存在线程同步问题,解决多线程同步问题必须要用锁,通常使用自旋锁,自旋锁是内核中提供的一种高IRQL锁,用同步以及独占的方式访问某个资源. 首先以简单的链表为案例,链表主要分为单向链表与双向链表,单向链表的链表节点中只有一个链表指针,其指向后一个链表元素,而双向链表节点中有两个链表节

随机推荐