链表系列之基础概念

一、链表与数组

我们最为常见的两个数据结构应该就是链表和数组了。

1、数组

所谓数组就是存储一系列相同类型,内存存储空间连续的一种线性表数据结构。

  • 存储相同类型,并且每个值都有对应的序号(index)访问
  • 内存空间连续,创建数组时,指定内存大小

图形示意:

 

2、链表

链表就是存储一系列相同类型的值,存储空间并不连续,上一个节点的指明下个节点的地址的一种数据结构。从三个方面来理解链表:

  • Value:每个Node都存储了一个具体类型的数据
  • Next:每个Node都存储了下个Node的地址
  • LinkedList:所有Node依次链接,就形成了LinkedList

图形示意:

3、数组 vs 链表

数组和链表对比,不难发现以下几个特点:

  • 数组需要连续的内存空间,而链表不需要,所以如果内存空间不足,往数组插入数据就会产生大块的数据迁移,会影响性能。
  • 由于数组每个index对应一个值,数组更易于查询,时间复杂度在O(1), 而链表查询只能一个个遍历,时间复杂度在O(n)
  • 链表优势在于插入,删除,时间复杂度都在O(1), 而数据中的插入删除,都会涉及到数据的遍历,时间复杂度在O(n),另外还涉及到数据的迁移,影响性能。
  • 数组的内存空间是创建时指定的,而链表是动态分配内存空间

 到底选择数组还是链表,大家可以根据自己的业务场景做分析。

二、链表种类

目前涉及的链表种类,总共有三种,分别是单链表,循环链表和双向链表,下面给大家一一讲解。

1、单链表

这是最常用的一个链表类型。节点的next指针仅仅指向下一个节点,依次链接,直到尾节点。

图例:

代码示例:

type ListNode struct {
	data interface{}
	next  *ListNode
}

2、循环链表

与单链表唯一的区别是,链表的尾节点,指向头节点。其带来的一个好处就是尾节点可以直接找到头节点,某些特定的问题,使用这种结构会比较简洁,比如约瑟夫问题

代码示例:

type ListNode struct {
	data interface{}
	next  *ListNode
}

3、双链表

与单链表相比,每个节点增加了一个prev指针,指向上一个节点的地址,提供了双向遍历的可能性。

代码示例:

type ListNode struct {
	data interface{}
	next  *ListNode
        prev  *ListNode
}

三、链表的操作

在实际应用中,单链表是最常用的,徒手写单链表,也成了面试的经典考题。我们着重的聊一下单链表的插入,删除,以及后续的一些经典问题解决。比如链表倒置,是否有环,寻找中间值等等

1、插入节点

创建一个新节点B,假设左边节点是A, 右边节点是C,在A和C之间插入节点B,该如何操作

 

 

第一步: 将B的Next指向C

C = A.next
B.next = C

第二步:将A的next地址指向B

A.next = B

2、删除节点

现有一个链表,三个节点A->B->C,如果要删除B,该如何操作:

第一步:将A的Next指向C

C = B.next
A.next = C

第二步:将B的Next断掉,既置为null

B.next = nil

四、总结

链表的代码的书写,比较考验逻辑能力,如果之前没怎么接触过,或者不熟悉其实很难写的出来,这是正常现象。如果要想熟悉,只有多练习,没有其他办法。练习时,需要多从以下几个点多想想:

1、链表的边界条件的处理,比如头节点和尾节点

2、很多链表问题可以采用快慢两个指针实现,如果没有思路时,可以试试两个指针

3、在链表操作的时候,注意指针的内存释放,免得泄露

4、大脑想不出来时,就画图帮助。

链表还有很多其他有意思的问题,后面我会在其他文章一一讲解,我会将实现代码传到github上,有需要的可以看看。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值