链表(Linked list)是一种常见数据结构,但并不会按线性的顺序存储数据,而是在每一个节点里存到下一个节点的指针。

由于不必须按顺序存储,链表在插入的时候可以达到O(1),比顺序表快得多,但是查找一个节点或者访问特定编号的节点则需要O(n)的时间。

Go语言的container包中,目前有heap,list,ring三种数据结构。

Heap 堆 (可实现最大堆和最小堆)
List 双向链表
Ring 环形链表

链表实现标准库的container/list代码包中。
代码包中2个程序实体List和Element
List:实现一个双向链表
Element:代表链表中元素的结构
List实现了一个双向链表(每个数据结点中都有两个指针,分别指向直接后继和直接前驱。所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点。)

特点1:链表占用的内存空间要比包含相同元素的数组所占的内存要大得多,因为其元素不是连续存储的,相邻元素之间需要保存对方的指针。
特点2:每个元素要保存所属链表的指针,在初始化时就拥有了头部元素(根元素),还记录了链表的长度。

面试题:可以把自己生成的Element类型值传给链表吗?

List有4种方法:
MoveBefore:把给定元素移动到另一个元素前面
MoveAfter:把给定元素移动到另一个元素后面
MoveFront:把给定元素移动到链表的最前端
MoveToBack:把给定元素移动到链表的最后端

func (l *List) MoveBefore(e, mark *Element)
func (l *List) MoveAfter(e, mark *Element) func (l *List) MoveToFront(e *Element)
func (l *List) MoveToBack(e *Element)

“给定的元素”都是*Element类型,即Element类型的指针类型,该指针类型的值就是元素的指针。

回答:对于我们在代码中生成的值,链表不会接受,也不会对链表产生任何影响。

因为代码生成的Element值并不在链表中,而且链表也不允许我们把自己生成的Element值插入链表。

问题解析:
List类型中插入新元素的方法只接受interface{}类型的值,这些方法内部会使用Element值包装接受到的新元素。这样做避免了直接使用我们自己生成的元素,也避免了链表的内部关联遭到破坏。

List.Front:获取链表中最前端元素,理解为List.GetFront
List.Back:获取链表中最后端元素,理解为List.GetBack
List.InsertBefore:在指定的元素之前插入新元素
List.InsertAfter:在指定的元素之后插入新元素
List.PushFront:在链表的最前端插入新元素
List.PushBack:在链表的最后端插入新元素

func (l *List) Front() *Element
func (l *List) Back() *Element func (l *List) InsertBefore(v interface{}, mark *Element) *Element
func (l *List) InsertAfter(v interface{}, mark *Element) *Element func (l *List) PushFront(v interface{}) *Element
func (l *List) PushBack(v interface{}) *Element

这些方法返回的都是Element值的指针(*Element),它们也是链表给出的安全接口,有了这些内部元素的指针,我们就可以去调用那些用于移动元素的方法了。

扩展知识:

1、问题:为什么链表可以做到开箱即用?

List这个“结构体类型”(关键字struct)有两个字段:

一个是Element类型的字段root(根元素),
一个是int类型的字段len(链表长度),它们属于包级私有,使用者无法查看和修改。

例如:

var l list.List

l有字段root和len,都被赋予相应的零值,len为0,表示该链表未包含任何元素;root的零值表示该类型的空壳,用字面量表示为:Element{}。

Element类型包含几个包级私有字段,分别用于存储前一个元素、后一个元素以及所属链表的指针值;

还有一个Value公开字段,该字段代表了持有元素的实际值,也是interface{}类型。在Element类型的零值中,这些字段的默认值都是nil。

延迟初始化:

延迟初始化就是把初始化操作延后,仅在实际需要使用的时候才进行,可以分散初始化操作过于集中带来的计算量和存储空间的密集消耗,优点就在于“延后”。

例如,如果我们在初始化时,需要集中声明大量的大容量切片的时候,此时CPU和内存空间的使用会激增,

只有设法让其中的切片及其底层数组被及时回收,内存的使用量才会有所降低。

而延迟初始化操作,可以将计算量和存储空间的压力分散到实际使用它们的时候,这些数组或切片被实际使用的时间越分散,延迟初始化带来的优势会越明显。

但是,延迟初始化的缺点,也在于“延后”,试想如果在调用链表的每个方法的时候,都需要去判断链表是否已经被初始化,那么也是一个计算量上的浪费。

这些方法被调用的越频繁,程序的性能也会降低的越多。

在这些链表实现方法中,Front方法和Back方法无需对是否初始化做出判断,因为一旦发现链表的长度为0就直接返回nil了。

又如,在删除元素、移动元素,以及一些插入元素的方法时,只要判断一下传入的元素指向所属链表的指针,是否与当前链表的指针相等就可以了。

如果不相等,就说明传入的元素不是这个链表的,就可以不用进行后续操作。反之,就一定说明这个链表已经被初始化了。

原因在于,链表的PushFront,PushBack,PushBackList,PushFrontList方法总会先判断链表的状态,并在必要时进行初始化,这就是延迟初始化;

而且,在向一个空链表中添加新元素时,必定会先调用这四个方法中的一个,这时新元素中指向所属链表的指针,一定会被设定为当前链表的指针,

所以说,链表初始化后当前指针肯定会等于新元素指针。

List类型利用了自身以及Element在结构上的特点,平衡了“延迟初始化”的利弊,使得链表可以“开箱即用”,并在性能上达到最优。

2、问题:Ring与List的区别在哪儿?

Ring类型是一个循环链表,也叫“环”。

List在内部也是一个循环链表根元素永远不会持有任何实际的元素值,而该元素的存在,就是为了连接这个循环链表的首尾两端。

所以说,List的零值是一个只包含了根元素root,但不包含任何实际元素值的空链表。

Ring和List的区别:

1、Ring类型的数据结构由它自身即可代表,而List类型则需要由它及Element类型联合表示;
2、一个Ring类型的值只代表了其所属的循环链表中的一个元素,而一个List类型的值则代表了一个完整的链表;
3、在创建并初始化(New函数)一个Ring值时可以指定它包含的元素的数量,而一个List值却不需要。循环链表一旦被创建,其长度是不可变的。
4、通过ring.Ring声明的变量将会是一个长度为l的循环链表,而List类型的零值则是一个长度为0的链表。
5、Ring值的Len方法的算法复杂度是O(N),而List值的Len方法复杂度则是O(1),性能上差别较大。

思考题:
1、container/ring包中的循环链表的适用场景有哪些?

Ring用于保持固定数量的元素,保存最近10天日志,最近5天登陆信息等。

08-1 Ring环形链表(另外总结)

2、使用过container/heap包中的堆吗?适用场景有哪些?
Heap用于排序。

本学习笔记仅为了总结自己学到的Go语言核心知识,方便以后回忆,文中部分内容摘录自极客时间的《Go语言核心36讲》专栏,如有侵权,请联系我删除。

[Golang学习笔记] 08 链表的更多相关文章

  1. golang学习笔记20 一道考察对并发多协程操作一个共享变量的面试题

    golang学习笔记20 一道考察对并发多协程操作一个共享变量的面试题 下面这个程序运行的能num结果是什么? package main import ( "fmt" " ...

  2. golang学习笔记19 用Golang实现以太坊代币转账

    golang学习笔记19 用Golang实现以太坊代币转账 在以太坊区块链中,我们称代币为Token,是以太坊区块链中每个人都可以任意发行的数字资产.并且它必须是遵循erc20标准的,至于erc20标 ...

  3. golang学习笔记18 用go语言编写移动端sdk和app开发gomobile

    golang学习笔记18 用go语言编写移动端sdk和app开发gomobile gomobile的使用-用go语言编写移动端sdk和app开发https://blog.csdn.net/u01249 ...

  4. golang学习笔记17 爬虫技术路线图,python,java,nodejs,go语言,scrapy主流框架介绍

    golang学习笔记17 爬虫技术路线图,python,java,nodejs,go语言,scrapy主流框架介绍 go语言爬虫框架:gocolly/colly,goquery,colly,chrom ...

  5. golang学习笔记16 beego orm 数据库操作

    golang学习笔记16 beego orm 数据库操作 beego ORM 是一个强大的 Go 语言 ORM 框架.她的灵感主要来自 Django ORM 和 SQLAlchemy. 目前该框架仍处 ...

  6. golang学习笔记14 golang substring 截取字符串

    golang学习笔记14 golang substring 截取字符串golang 没有java那样的substring函数,但支持直接根据 index 截取字符串mystr := "hel ...

  7. golang学习笔记13 Golang 类型转换整理 go语言string、int、int64、float64、complex 互相转换

    golang学习笔记13 Golang 类型转换整理 go语言string.int.int64.float64.complex 互相转换 #string到intint,err:=strconv.Ato ...

  8. golang学习笔记12 beego table name `xxx` repeat register, must be unique 错误问题

    golang学习笔记12 beego table name `xxx` repeat register, must be unique 错误问题 今天测试了重新建一个项目生成新的表,然后复制到旧的项目 ...

  9. golang学习笔记11 golang要用jetbrain的golang这个IDE工具开发才好

    golang学习笔记11   golang要用jetbrain的golang这个IDE工具开发才好  jetbrain家的全套ide都很好用,一定要dark背景风格才装B   从File-->s ...

随机推荐

  1. 第三课 java编程入门

    java特点: 1.面对象性 2.可移植性/跨平台性 java组成: jdk(java工具开发工具包) /       \              \ jre       指令集合   api和常用 ...

  2. 5 Dockerfile指令详解 && CMD 指令

    CMD 指令的格式和 RUN 相似,也是两种格式: shell 格式: CMD <命令> exec 格式: CMD ["可执行文件", "参数1", ...

  3. C# 表达式树 创建、生成、使用、lambda转成表达式树~表达式树的知识详解

    笔者最近学了表达式树这一部分内容,为了加深理解,写文章巩固知识,如有错误,请评论指出~ 表达式树的概念 表达式树的创建有 Lambda法 和 组装法. 学习表达式树需要 委托.Lambda.Func& ...

  4. layer 遮罩层等待

    效果 代码: js函数之前:var msg = layer.msg('努力中加载中...', {icon: 16,shade: [0.5, '#f5f5f5'],scrollbar: false,of ...

  5. webbench 网站压力测试

    [root@localhost ~]# webbench -c 500 -t 4 http://172.24.61.41/Webbench - Simple Web Benchmark 1.5Copy ...

  6. July 30th 2017 Week 31st Sunday

    Eternity is not a distance, but a decision. 永恒不是一段距离,而是一种决定. What can be called as eternity? Wealth ...

  7. 荣禄[róng lù]

    荣禄[róng lù] 百科名片 荣禄 荣禄(1836年4月6日-1903年4月11日)清末大臣,晚清政治家.字仲华,号略园,瓜尔佳氏,满洲正白旗人,出身于世代军官家庭,以荫生晋工部员外郎,后任内务府 ...

  8. 41、Thead线程 System.Thread与互斥体Mutex

    Thead线程 System.Thread 使用Thread类可以创建和控制线程.下面的代码是创建和启动一个新线程的简单例子.Thread 类的构造函数重载为接受ThreadStart和Paramet ...

  9. 由JDK源码学习ArrayList

    ArrayList是实现了List接口的动态数组.与java中的数组相比,它的容量能动态增长.ArrayList的三大特点: ① 底层采用数组结构 ② 有序 ③ 非同步 下面我们从ArrayList的 ...

  10. CSS(层叠样式表)基础知识

     CSS 指层叠样式表 (Cascading Style Sheets).样式定义怎样显示 HTML 元素.它通常存储在样式表中,把样式加入到 HTML 4.0 中,解决内容与表现分离的问题. 当同一 ...