链表

底层存储

数组:一块连续的内存空间。
链表:用指针串起来的一组零散的内存空间。
链表
链表分类:单链表、双向链表、循环链表
内存块是连接的节点。每个节点存储数据和指向下一个节点的指针。第一个节点叫做头结点,最后一个节点叫做尾节点。尾节点的next=null。
链表

操作特点

1 插入、删除很快,O(1)。
2 想要随机访问只能从头节点开始遍历,数到第k个节点。平均时间复杂度O(n)。想象队列就是一个排队的队伍。每个人只知道自己后面的那个人。

循环链表

结构特点:循环列表的尾节点指向链表的头结点。
链表
用途:用于解决数据具有环形结构的问题。例如约瑟夫问题。

双向链表

双向链表的节点存储:数据+指向下一个节点的指针+指向上一个节点的指针。
链表

用途1:更方便的插入删除

删除的使用场景一般为:给定一个值,将具有这个值得节点删除。只是删除需要O(1)的时间,但是查询的平均时间复杂度为O(n)。根据时间复杂度的加法原则,时间复杂度为O(n)。
另外一个场景是:已经给定一个节点,删除这个节点。单项链表因为没有向前的指针,所以需要从头开始遍历,找到要删除节点的前一个节点。平均时间复杂度(O(n))。如果是双向列表,因为有向前的指针,O(1)就可以实现。

在某个节点前插入一个元素,双向列表显示出了优势,因为有前向指针。时间为O(1)。

用途2:在有序列表中查找更方便

因为每次查询时,都要比较当前元素和要找元素的大小,然后决定向前走,还是向后走。

思考题:如何基于链表给出LRU缓存

LRU缓存:是指删除最近访问少的元素 的缓存策略。
已经访问过的元素用链表存储。现在假设访问元素a,有如下情况:
1 a在链表中,那把a元素删除,插入在队列头部作为头结点。
2 a不在链表中
2.1 并且链表空间充足,则把a插入在链表头部。
2.2 并且链表空间不足,则把尾元素删除,把a插入在链表头部。
因为要遍历每个元素,所以平均时间复杂度是O(n)。如果使用散列表就能提高速度。

写好链表代码的几个技巧

1 理解指针的含义。将某个变量赋值给指针,实际上就是将这个变量的地址赋值给指针。
2 警惕指针丢失和内存泄漏
3 利用哑结点简化代码
4 留意边界条件的处理。链表可以考虑的几个边界条件有:
4.1 如果列表为空,代码可以正常运行吗?
4.2 如果列表只包含一个节点,代码可以正常运行吗?
4.2 如果列表含2个节点,代码可以正常运行吗?
4.3 代码逻辑在处理头、尾节点的时候,代码可以正常运行吗?

5 举例画图帮助思考
6 多写多练