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

yizhihongxing

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

什么是单链表

单链表是一种常见的数据结构,它由一系列节点组成。每个节点分为两个部分,第一个部分存储当前节点的值,第二个部分存储下一个节点的地址。最后一个节点指向空(null)。单链表中保存的数据不存在顺序关系,且每个节点仅知道下一个节点的地址,不知道前一个节点的地址。因此,无法随机访问单链表中的元素,只能从链表的头部一个一个地遍历各个节点。

在Go语言中实现单链表

定义单链表节点的结构体

在Go语言中,可以使用结构体来定义单链表中的节点。结构体包含两个属性:Value表示当前节点存储的值,Next表示下一个节点的地址。

type ListNode struct {
    Value interface{} // 存储的值,使用interface{}可以保存任何类型的数据
    Next  *ListNode   // 下一个节点的地址
}

定义单链表结构体

定义单链表结构体,包含头结点和当前链表长度。头结点不存储任何值,仅用来标识链表的开始位置。

type LinkedList struct {
    head  *ListNode // 头结点
    count int       // 当前链表长度
}

实现单链表的初始化方法

初始化单链表时,仅需要创建一个头结点,并将链表长度设置为0。

func (list *LinkedList) Init() {
    list.head = new(ListNode) // 创建头结点
    list.count = 0           // 链表长度为0
}

实现单链表的获取长度方法

链表长度是记录在链表结构体中的一个属性,只需要返回该属性的值即可。

func (list *LinkedList) Len() int {
    return list.count
}

实现单链表的添加节点方法

链表中最常用的操作之一是添加节点。在单链表中,添加节点指的是将新节点插入到链表的末尾。

func (list *LinkedList) Append(value interface{}) {
    node := &ListNode{Value: value} // 创建新节点
    tail := list.head

    for tail.Next != nil {
        tail = tail.Next // 获取最后一个节点的地址
    }

    tail.Next = node // 将新节点添加到链表的末尾
    list.count++     // 更新链表长度
}

实现单链表的查找节点方法

在单链表中,查找节点指的是根据节点的值,从头结点开始寻找该节点。

func (list *LinkedList) Find(value interface{}) *ListNode {
    cur := list.head.Next // 从第一个节点开始查找

    for cur != nil {
        if cur.Value == value {
            return cur // 查找到节点
        }

        cur = cur.Next // 继续查找下一个节点
    }

    return nil // 没有找到节点
}

示例1:使用单链表保存任务列表

func main() {
    // 初始化单链表
    taskList := new(LinkedList)
    taskList.Init()

    // 添加任务到链表中
    taskList.Append("任务1")
    taskList.Append("任务2")
    taskList.Append("任务3")
    taskList.Append("任务4")

    // 在任务列表中查找指定任务
    task := taskList.Find("任务3")
    if task != nil {
        fmt.Println("找到任务:", task.Value)
    } else {
        fmt.Println("未找到任务")
    }
}

示例2:使用单链表实现LRU缓存

type LRUCache struct {
    cap     int           // 缓存容量
    cache   map[int]*Node // 缓存中的节点
    head    *Node         // 头结点
    tail    *Node         // 尾节点
    current int           // 当前链表长度
}

type Node struct {
    key   int  // 缓存键
    value int  // 缓存值
    prev  *Node
    next  *Node
}

func NewLRUCache(cap int) *LRUCache {
    cache := &LRUCache{
        cap:   cap,
        cache: make(map[int]*Node),
        head:  new(Node),
        tail:  new(Node),
    }

    cache.head.next = cache.tail
    cache.tail.prev = cache.head

    return cache
}

func (cache *LRUCache) Put(key int, value int) {
    node, ok := cache.cache[key]

    if !ok {
        node = &Node{key: key, value: value}
        cache.cache[key] = node
        cache.current++

        if cache.current > cache.cap {
            cache.Remove(cache.tail.prev)
        }
    } else {
        node.value = value
        cache.Remove(node)
    }

    cache.MoveToFront(node)
}

func (cache *LRUCache) Remove(node *Node) {
    node.prev.next = node.next
    node.next.prev = node.prev
    delete(cache.cache, node.key)
    cache.current--
}

func (cache *LRUCache) MoveToFront(node *Node) {
    node.prev = cache.head
    node.next = cache.head.next
    cache.head.next.prev = node
    cache.head.next = node
}

func (cache *LRUCache) Get(key int) int {
    node, ok := cache.cache[key]

    if !ok {
        return -1
    }

    cache.MoveToFront(node)

    return node.value
}

以上是单链表及其常用方法的实现。通过示例,可以看到单链表的应用非常广泛,可以用于任务列表、LRU缓存等场景。本文实现的单链表类似于Go标准库中的container/list包,读者可以自行比较并学习。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:详解go语言单链表及其常用方法的实现 - Python技术站

(0)
上一篇 2023年6月27日
下一篇 2023年6月27日

相关文章

  • 关于.net的c#:32位块密码

    以下是关于“.NET的C#:32位块密码”的完整攻略,包含两个示例。 关于.NET的C#:32位块密码 在.NET的C#中我们可以使用System.Security.Cryptography命名空间中的类来实现32位块密码。以下是关于如何实现32位块密码的详细攻略。 1. 实现32位块密码 在.NET的C#中,我们可以使用AesManaged类来实现32位块…

    other 2023年5月9日
    00
  • 使用Windows批处理和WMI设置Python的环境变量方法

    关于“使用Windows批处理和WMI设置Python的环境变量方法”的完整攻略,以下是详细的步骤和示例说明: 1. 了解Windows批处理和WMI Windows批处理(Batch)是指一类以批量处理命令为基础的脚本语言。在Windows操作系统中,可以使用Windows批处理快速进行一系列操作,例如安装程序、打开应用、复制文件等等。WMI(Window…

    other 2023年6月27日
    00
  • 解析结构体的定义及使用详解

    解析结构体的定义及使用详解 在编程中,结构体是一种自定义的数据类型,它可以包含多个不同类型的数据成员。解析结构体是一种特殊的结构体,它用于存储和处理解析后的数据。本攻略将详细介绍解析结构体的定义和使用方法,并提供两个示例说明。 定义解析结构体 解析结构体的定义与普通结构体的定义类似,但通常会包含用于解析数据的特定字段。以下是定义解析结构体的一般语法: str…

    other 2023年8月8日
    00
  • JavaScript数据结构之双向链表

    JavaScript数据结构之双向链表是一种常见的数据结构,既可以用于解决实际问题,也可以用于加深对数据结构和算法的理解。下面是这个主题的完整攻略。 概念 双向链表是一种链式存储结构,每个节点包含指向前驱节点和后继节点的指针。相比单向链表,双向链表具有可以双向遍历、插入和删除节点等优势,但同时也存在一些缺点,如结构复杂,占用内存多等。 实现 以下是JavaS…

    other 2023年6月27日
    00
  • 如何给虚拟机提速

    如何给虚拟机提速攻略 虚拟机的性能提升可以通过多种方式实现。下面是一些可以帮助您提升虚拟机性能的方法和示例说明。 1. 分配更多的资源 虚拟机的性能受到分配给它的资源的限制。通过增加虚拟机的资源分配,可以提高其性能。 示例说明: 增加内存分配:在虚拟机管理软件中增加虚拟机的内存分配。例如,将虚拟机的内存从2GB增加到4GB,可以提高虚拟机的运行速度和响应能力…

    other 2023年8月1日
    00
  • Android 自定义View手写签名并保存图片功能

    Android 自定义View手写签名并保存图片功能 本攻略将详细介绍如何在Android应用中实现自定义View手写签名并保存图片的功能。 步骤一:创建自定义View 首先,我们需要创建一个自定义View来实现手写签名的功能。可以继承View类或者使用现有的绘图库,如Canvas和Paint。 示例代码: public class SignatureVie…

    other 2023年10月13日
    00
  • docker修改容器配置文件的3种方法总结

    关于“docker修改容器配置文件的3种方法总结”的攻略,具体步骤如下: 1. 进入容器进行修改 这种方法需要先进入容器,然后修改配置文件,再退出容器,最后重新启动容器使修改生效。 步骤如下: 使用docker exec命令进入容器:docker exec -it container_name /bin/bash 切换到需要修改配置文件的目录:cd dire…

    other 2023年6月25日
    00
  • TypeScript利用TS封装Axios实战

    下面是“TypeScript利用TS封装Axios实战”的完整攻略: 前置要求 在开始使用TypeScript封装Axios前,需要确保已经安装并了解以下知识: Node.js:用于在本地运行TypeScript和生成JavaScript文件。 TypeScript:在Node.js环境下编写TypeScript代码,需要先进行TypeScript的安装和配…

    other 2023年6月25日
    00
合作推广
合作推广
分享本页
返回顶部