Go-如何优雅的实现单链表?(含全部代码)

简介: Go-如何优雅的实现单链表?(含全部代码)

简介

链表是一种链式存取的数据结构,用一组地址任意的存储单元存放线性表中的数据元素。链表中的数据是以结点来表示的,每个结点的构成是元素加指针。

思路

2020062310470442.png为了能够兼容所有类型,采用空接口。为了更好的封装,用"私有"的next和insert、remove等,看下方详细解释。

节点结构体

属性

type ListNode struct {
    Val interface{}
    next *ListNode
    lst *SingleList
}
  • Val:节点的值
  • next:下一节点指针
  • lst:所属单链表

Val采用空接口,接收任意数据类型,可通过类型断言再赋给对应类型。

方法

返回下一个节点,O(1)

func (node *ListNode)Next() *ListNode {
  return node.next
}

主要用于遍历,我在其他方法中也有调用。

单链表结构体

属性

type SingleList struct {
  head *ListNode
  len int
}
  • head:头结点
  • len:单链表长度

这里其实可以将head的Val用来存储长度,可以稍微节省一点内存,由于还需要类型断言,比较麻烦,就算了

方法

Init

func (lst *SingleList)Init(){
  lst.head = new(ListNode)
  lst.head.lst = lst
  lst.len = 0
}

创建头结点,长度为0,O(1)

New

func New() *SingleList {
  lst := new(SingleList)
  lst.Init()
  return lst
}

返回创建的单链表,调用了Init,O(1)

Clear

func (lst *SingleList)Clear(){
  lst.Init()
}

调用Init,利用Go自己的垃圾回收,O(1)

Len

func (lst *SingleList)Len() int {
  return lst.len
}

返回单链表长度,O(1)

Front

func (lst *SingleList)Front() *ListNode {
  return lst.head.Next()
}

Back

func (lst *SingleList)Back() *ListNode {
  if lst.len == 0{
    return nil
  }
  cur := lst.head.Next()
  for{
    if cur.Next() == nil{
      return cur
    }
    cur = cur.Next()
  }
}

返回最后一个节点,空链表的情况下为nil,O(n)

insert

func (lst *SingleList)insert(e, at *ListNode) *ListNode{
  lst.len++
  e.next = at.next
  at.next = e
  e.lst = lst
  return e
}

返回指向插入的节点e的指针。

  • e:待插入节点
  • at:链表

仅在此函数进行长度增加,其他插入函数调用此函数,O(1)。

InsertAfter

1.func (lst *SingleList)InsertAfter(val interface{}, mark *ListNode) *ListNode {
  if mark == nil{
    return nil
  }
  if mark.lst == lst{
    return lst.insert(&ListNode{Val:val},mark)
  }
  return nil
}

生成值为val的节点,并插入链表节点mark后,返回插入的节点,mark为nil或不属于单链表的情况下,返回nil。调用了insert,O(1)

  • val:待插节点值
  • mark:在此节点后插入,如果此节点属于单链表lst

PushBack

func (lst *SingleList)PushBack(val interface{}) *ListNode{
  end := lst.Back()
  if end == nil{
    return lst.InsertAfter(val,lst.head)
  }else {
    return lst.InsertAfter(val,end)
  }
}

生成值为val的节点,并插入链表尾节点后,调用Back和InsertAfter。返回指向生成节点的指针,O(n)。

  • val:待插节点值

PushFront

func (lst *SingleList)PushFront(val interface{}) *ListNode {
  return lst.InsertAfter(val,lst.head)
}

生成值为val的节点,并插入链表头节点后,调用InsertAfter。返回指向生成节点的指针,O(1)。

  • val:待插节点值

remove

func (lst *SingleList)remove(e *ListNode) *ListNode{
  nextOne := e.next
  if nextOne == nil{
    return nil
  }
  lst.len--
  e.next = e.next.next
  nextOne.lst = nil
  return nextOne
}

删除e的下一个节点,返回删除的节点,仅在此函数进行长度减少,其他删除函数调用此函数。e是尾节点时返回nil,O(1)。

  • e:单链表上的节点,需删除它后面的一个节点

RemoveAfter

func (lst *SingleList)RemoveAfter(e *ListNode) *ListNode{
  if e == nil{
    return nil
  }
  if e.lst == lst{
    return lst.remove(e)
  }
  return nil
}

删除指定节点的后一个节点,返回删除的节点,指定元素为尾节点或nil时,返回nil,O(1)

RemoveFront

func (lst *SingleList)RemoveFront() *ListNode {
  return lst.remove(lst.head)
}

    测试

    2020062310470442.png

    常见问题

    怎么遍历呀?

    像下面这样就可以了呦

    for e := lst.Front(); e != nil; e = e.Next() {
       do something with e.Val
    }

    全部代码

    具体代码和测试代码在:https://gitee.com/frankyu365/datastructure

    参考

    Go标准库-container/list

    更多Go相关内容:Go-Golang学习总结笔记

    有问题请下方评论,转载请注明出处,并附有原文链接,谢谢!如有侵权,请及时联系。



    相关文章
    |
    Go 索引
    掌握Go语言:Go语言范围,优雅遍历数据结构,简化代码操作实战解析(24)
    掌握Go语言:Go语言范围,优雅遍历数据结构,简化代码操作实战解析(24)
    114 0
    |
    Cloud Native Go 开发工具
    不改一行代码轻松玩转 Go 应用微服务治理
    为了更好的进行 Go 应用微服务治理,提高研发效率和系统稳定性,本文将介绍 MSE 微服务治理方案,无需修改业务代码,实现治理能力。
    20030 98
    |
    算法 程序员 编译器
    美丽的代码:规范go应用代码注释
    【6月更文挑战第30天】本文介绍注释应与代码同步,避免误导,且关键点解释。使用LLVM构建编译器示例展示Go语言规范。注释虽有局限,但在解释复杂逻辑、业务规则时仍有其价值。程序员需平衡注释与代码的关系,创造更优的代码。
    1202 0
    美丽的代码:规范go应用代码注释
    |
    10月前
    |
    安全 Go 开发者
    代码之美:Go语言并发编程的优雅实现与案例分析
    【10月更文挑战第28天】Go语言自2009年发布以来,凭借简洁的语法、高效的性能和原生的并发支持,赢得了众多开发者的青睐。本文通过两个案例,分别展示了如何使用goroutine和channel实现并发下载网页和构建并发Web服务器,深入探讨了Go语言并发编程的优雅实现。
    168 2
    |
    10月前
    |
    SQL 监控 算法
    为Go应用无侵入地添加任意代码
    这篇文章旨在提供技术深度和实践指南,帮助开发者理解并应用这项创新技术来提高Golang应用的监控与服务治理能力。在接下来的部分,我们将通过一些实际案例,进一步展示如何在不同场景中应用这项技术,提供更多实践启示。
    |
    11月前
    |
    JSON 搜索推荐 Go
    ZincSearch搜索引擎中文文档及在Go语言中代码实现
    ZincSearch官网及开发文档均为英文,对非英语用户不够友好。GoFly全栈开发社区将官方文档翻译成中文,并增加实战经验和代码,便于新手使用。本文档涵盖ZincSearch在Go语言中的实现,包括封装工具库、操作接口、统一组件调用及业务代码示例。官方文档https://zincsearch-docs.zinc.dev;中文文档https://doc.goflys.cn/docview?id=41。
    392 0
    |
    缓存 NoSQL 数据库
    go-zero微服务实战系列(五、缓存代码怎么写)
    go-zero微服务实战系列(五、缓存代码怎么写)
    |
    程序员 测试技术 Go
    用 Go 编写简洁代码的最佳实践
    用 Go 编写简洁代码的最佳实践
    |
    缓存 测试技术 Go
    使用Singleflight优化Go代码
    使用Singleflight优化Go代码