第一节 如何用Go实现单链表
一、概念介绍
下面这副图是我们单链表运煤车队。

每节运煤车就是单链表里的元素,每节车厢里的煤炭就是元素中保存的数据。前后车通过锁链相连,作为单链表运煤车,从1号车厢开始,每节车厢都知道后面拉着哪一节车厢,却不知道前面是哪节车厢拉的自己。第一节车厢没有任何车厢拉它,我们就叫它车头,第五节车厢后面拉其他车厢,我们称为车尾。
作为单链表它最大的特点就是能随意增加车队的长度,也能随意减少车队的长度。这是比数组公交车最大的优点。
二、Go语言实现讲解
1、节点

每节车厢都由车体、绳索和煤炭构成。在Go语言中表示这种自定义组合体的类型就是结构,当然为了通用性,我们这里要把车厢转换成节点也就是元素,煤炭转换成数据,绳索转换成指针。
type Node struct {
data Object
next *Node
}
用张图来描述这种对应关系。

这里结构体Node表示车厢,data表示煤炭用Object类型,next是牵着下节车厢的绳索,用指针表示。
对于车厢来说,除了放煤炭外,还能放瓜果、衣物、饭菜等等,所以这里data的类型必须通用,当然Go里是没有Java里的Object类型的,所以我们就自己定义了一个。
type Object interface{}
2、链表

光有车厢还不够,我们还要描述车厢组成的车队。每个单链表车队都有车头、车尾和车厢数量,我们同样以结构来表现。
type List struct {
size uint64 // 车辆数量
head *Node // 车头
tail *Node // 车尾
}
3、方法
(1)初始化
第一步就是组装单链表车队,这就是初始化。不过开始的时候车队是个空壳,一个车厢没有。
func (list *List) Init() {
(*list).size = 0 // 此时链表是空的
(*list).head = nil // 没有车头
(*list).tail = nil // 没有车尾
}
(2)添加元素
当前的单链表是空的,所以我们要向里面添加元素。
func (list *List) Append(node *Node) {
(*list).head = node // 这是单链表的第一个元素,也是链表的头部
(*list).tail = node // 同时是单链表的尾部
(*list).size = 1 // 单链表有了第一个元素
}
现在单链表有了第一个元素,我还想再添加一个元素,当然是添加到单链表尾部。
func (list *List) Append(node *Node) {
if (*list).size == 0 { // 无元素的时候添加
(*list).head = node // 这是单链表的第一个元素,也是链表的头部
(*list).tail = node // 同时是单链表的尾部
(*list).size = 1 // 单链表有了第一个元素
} else { // 有元素了再添加
oldTail := (*list).tail
(*oldTail).next = node // node放到尾部元素后面
(*list).tail = node // node成为新的尾部
(*list).size++ // 元素数量增加
}
}
分析上面的代码存在3处疑点,元芳你怎么看?属下认为
第一,node如果为空,则添加无任何意义;
第二,代码中存在重复的地方;
这第三么,卑职如何才能知道新增结果?
下面大卫哥顺着元芳的思路改进下代码。
func (list *List) Append(node *Node) bool {
if node == nil {
return false
}
(*node).next = nil
// 将新元素放入单链表中
if (*list).size == 0 {
(*list).head = node
} else {
oldTail := (*list).tail
(*oldTail).next = node
}
// 调整尾部位置,及链表元素数量
(*list).tail = node // node成为新的尾部
(*list).size++ // 元素数量增加
return true
}
(3)插入元素
一天领导让大卫哥把他小舅子安排到第一个,于是大卫哥为了拍好领导马屁。绞尽脑汁弄了一个方法。
func (list *List) Insert(node *Node) bool {
if node == nil {
return false
}
(*node).next = (*list).head // 领导小舅子排到之前第一名前面
(*list).head = node // 领导小舅子成为第一名
(*list).size++
return true
}
来托关系插队的人越来越多,领导的关系户不能动,只能插后面人的队了。于是大卫哥又修改了代码,增加了位置参数。
func (list *List) Insert(i uint,node *Node) bool {
// 空的节点、索引超出范围和空链表都无法做插入操作
if node == nil || i > (*list).size || (*list).size == 0 {
return false
}
if i == 0 { // 直接排第一,也就领导小舅子才可以
(*node).next = (*list).head
(*list).head = node
} else {
// 找到前一个元素
preItem := (*list).head
for j := 1 ; j < i; j++ { // 数前面i个元素
preItem = (*preItem).next
}
// 原来元素放到新元素后面,新元素放到前一个元素后面
(*node).next = (*preItem).next
(*preItem).next = preItem
}
(*list).size++
return true
}
(4)删除元素
插队的关系户太多,影响了正常排队的人,被人投诉,大卫哥只好想办法删除一些。
func (list *List) Remove(i uint, node *Node) bool {
if i >= (*list).size {
return false
}
if i == 0 { // 删除头部
node = (*list).head
(*list).head = (*node).next
if (*list).size == 1 { // 如果只有一个元素,那尾部也要调整
(*list).tail = nil
}
} else {
preItem := (*list).head
for j := 1; j < i; j++ {
preItem = (*preItem).next
}
node = (*preItem).next
(*preItem).next = (*node).next
if i == ((*list).size - 1) { // 若删除的尾部,尾部指针需要调整
(*list).tail = preItem
}
}
(*list).size--
return true
}
(5)获取
为了获取某个位置的元素,我们需要一个方法。
func (list *List) Get(i uint) *Node {
if i >= (*list).size {
return nil
}
item := (*list).head
for j := 0; j < i ; j++ { // 从head数i个
item = (*item).next
}
return item
}
到这里基本框架已经出来了,不过还有几个接口还没实现,作为课后作业。
三、小结
单链表就和列车类似,一个接着一个,所以本节从列车类比介绍了单链表的Go语言实现。在接口实现部分大卫哥以序号作为链表中每个节点的操作关键字。在实际应用中,我们往往以data中的某一个字段作为操作关键字。所以这也衍生出链表的不同接口,大家可以参考大卫哥留的链接中的代码实现作为理解。同时有些实现将表头独立出来并不存放数据,这在一定程度上简化了代码的实现。
四、习题
(1)补全GetSize,RemoveAll,GetHead和GetTail的定义和实现。
(2)以data作为参数,考虑单链表的实现。
(3)将单链表的head独立出来,此时的head是独立的,不存放data,如下图,考虑单链表的实现,并比较这种实现。

(4)如果将head和tail都独立出来,都不存放data,此时的单链表如何实现?这样实现的代码在插入删除操作时候是不是更容易点?

第一节 如何用Go实现单链表的更多相关文章
- 动态单链表的传统存储方式和10种常见操作-C语言实现
顺序线性表的优点:方便存取(随机的),特点是物理位置和逻辑为主都是连续的(相邻).但是也有不足,比如:前面的插入和删除算法,需要移动大量元素,浪费时间,那么链式线性表 (简称链表) 就能解决这个问题. ...
- Java单链表反转 详细过程
版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/guyuealian/article/details/51119499 Java单链表反转 Java实 ...
- 【数据结构】单链表&&静态链表详解和代码实例
喜欢的话可以扫码关注我们的公众号哦,更多精彩尽在微信公众号[程序猿声] 01 单链表(Singly Linked List ) 1.1 什么是单链表? 单链表是一种链式存储的结构.它动态的为节点分配存 ...
- c++单链表基本功能
head_LinkNode.h /*单链表类的头文件*/#include<assert.h>#include"compare.h"typedef int status; ...
- C++ 单链表基本操作
链表一直是面试的高频题,今天先总结一下单链表的使用,下节再总结双向链表的.本文主要有单链表的创建.插入.删除节点等. 1.概念 单链表是一种链式存取的数据结构,用一组地址任意的存储单元存放线性表中的数 ...
- js数据结构与算法--单链表的实现与应用思考
链表是动态的数据结构,它的每个元素由一个存储元素本身的节点和一个指向下一个元素的引用(也称指针或链接)组成. 现实中,有一些链表的例子. 第一个就是寻宝的游戏.你有一条线索,这条线索是指向寻找下一条线 ...
- 数据结构(一) 单链表的实现-JAVA
数据结构还是很重要的,就算不是那种很牛逼的,但起码得知道基础的东西,这一系列就算是复习一下以前学过的数据结构和填补自己在这一块的知识的空缺.加油.珍惜校园中自由学习的时光.按照链表.栈.队列.排序.数 ...
- JAVA实现具有迭代器的线性表(单链表)
一,迭代器的基本知识: 1,为什么要用迭代器?(迭代:即对每一个元素进行一次“问候”) 比如说,我们定义了一个ADT(抽象数据类型),作为ADT的一种实现,如单链表.而单链表的基本操作中,大部分需要用 ...
- 数据结构C语言版--单链表的基本功能实现
/* * 构造一个链式存储的线性表(当输入9999时,结束构造过程),然后输出该线性表 * 并统计该线性链表的长度 . *注:new和delete是C++的运算符 malloc和free是C++/C的 ...
随机推荐
- @Autowired和@Resource注解的区别
@Autowired注解是按类型装配依赖对象,默认情况下它要求依赖对象必须存在,如果允许null值,可以设置它required属性为false.如果我们想使用按名称装配,可以结合@Qualifier注 ...
- 沉淀再出发:再谈java的多线程机制
沉淀再出发:再谈java的多线程机制 一.前言 自从我们学习了操作系统之后,对于其中的线程和进程就有了非常深刻的理解,但是,我们可能在C,C++语言之中尝试过这些机制,并且做过相应的实验,但是对于ja ...
- MS10-070 ASP.NET Padding Oracle信息泄露漏洞项目测试
MS10-070 ASP.NET Padding Oracle信息泄露漏洞1 漏洞描述:ASP.NET由于加密填充验证过程中处理错误不当,导致存在一个信息披露漏洞.成功利用此漏洞的攻击 ...
- Kali-linux使用SET实施攻击
前面介绍了社会工程学工具包(SET)的简单使用.为了能帮助用户更容易的理解社会工程学的强大功能.本节将介绍使用社会工程学工具包实施各种攻击. 7.4.1 针对性钓鱼攻击向量 针对性钓鱼攻击向量通过构造 ...
- 洛谷P1832 A+B Problem(再升级)
放题解 题目传送门 放代码 #include<bits/stdc++.h> using namespace std; ];//n为被分解数 a数组用于存储素数 ];//dp数组用于存储方案 ...
- windows环境安装docker,并下载lamp镜像
1.PC系统:windows10专业版 2.开启Hyper-V 此电脑->右击->属性->控制面板主页->(查看方式为小图标)程序和功能->右上方启动或关闭windows ...
- Java类加载机制与Tomcat类加载器架构
Java类加载机制 类加载器 虚拟机设计团队把类加载阶段中的“通过一个类的全限定名来获取描述此类的二进制字节流”这个动作放到Java虚拟机外部去实现,以便让应用程序自己决定如何去获取所需要的类.实现这 ...
- 对极几何(Epipolar Geometry)
基本概念 对极几何(Epipolar Geometry)是Structure from Motion问题中,在两个相机位置产生的两幅图像的之间存在的一种特殊几何关系,是sfm问题中2D-2D求解两帧间 ...
- POJ 3080 Blue Jeans 找最长公共子串(暴力模拟+KMP匹配)
Blue Jeans Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 20966 Accepted: 9279 Descr ...
- Angular动态表单生成(二)
ng-dynamic-forms源码分析 在两个开源项目中,ng-dynamic-forms的源码相较于form.io,比较简单,所以我还勉强能看懂,下面就我自己的理解进行简单分析,若有不对的地方,请 ...