链表(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. Linux NFS 详解

    目录:      1.了解NFS服务      2.NFS主要文件      3.NFS安装      4.NFS配置     5.NFS客户端配置 6.固定NFS端口      7.Windows挂 ...

  2. Homebrew 安装 MySQL

    安装 Homebrew brew doctor 确认 brew 在正常工作 brew update 更新包 brew install mysql 安装 MySQL ​ ==> Downloadi ...

  3. SuperSocket.ClientEngine介绍

    项目地址:https://github.com/kerryjiang/SuperSocket.ClientEngine 其中需要引入的SuperSocket.ProtoBase项目:SuperSock ...

  4. fzu_oop_east 第二次作业

    这次有四题: 题目1:(这题本身没难度,就是听说格式比较坑,好像) 代码: #include<iostream> #include<cstdio> using namespac ...

  5. MathUtils BigDecimal 数字工具类

    package com.hxqc.basic.dependency.util; import org.apache.commons.lang.StringUtils; import java.math ...

  6. 一句话解释:C2C / B2C / O2O / B2B

    C2C (Customers To Customers):个人到个人,就是我卖东西你来买B2C (Business To Customers):公司到个人,就是我成立个公司卖东西,你来买O2O (On ...

  7. 电脑需要重启才能连上WLAN

    我的笔记本电脑是Windows10 系统,在某次更新后发现这个问题,查资料过程中忽然断网,非要重启才能解决,非常恼人.经过一番研究,发现一个行之有效的解决方法. 1.打开设备管理器. 2.点击网络适配 ...

  8. window resize和scroll事件的基本优化

    转自 http://www.kankanews.com/ICkengine/archives/6564.shtml

  9. iostat查看系统的IO负载情况

    1.安装iostat工具: [root@localhost ~]# yum -y install sysstat 2.通过命令查看IO情况: %idle如果小于%70的话,说明磁盘的IO负载压力已经很 ...

  10. PAT——1048. 数字加密

    本题要求实现一种数字加密方法.首先固定一个加密用正整数A,对任一正整数B,将其每1位数字与A的对应位置上的数字进行以下运算:对奇数位,对应位的数字相加后对13取余——这里用J代表10.Q代表11.K代 ...