注:此专栏内容主要参考极客时间-数据结构与算法之美
1. 概念
链表与数组相反,它并不需要一块连续的内存空间,它通过“指针”将一组零散的内存块串联起来使用;
常见的三类链表:单链表、双向链表、循环链表
2.链表特点
- 链表中插入或者删除一个数据,我们并不需要为了保持内存的连续性而搬移结点,因为链表的存储空间本身就不是连续的。所以,在链表中插入和删除一个数据是非常快速的。
- 链表要想随机访问第 k 个元素,就没有数组那么高效了,,而是需要根据指针一个结点一个结点地依次遍历,直到找到相应的
结点。 - 链表随机访问的性能没有数组好,需要 O(n) 的时间复杂度
3. 单链表
第一个结点叫作头结点,把最后一个结点叫作尾结点。
头结点用来记录链表的基地址。 尾节点指针指向的是一个空地址Null;
4.循环链表
循环链表的尾结点指针是指向链表的头结点。
5.双向链表
双向链表,支持两个方向,每个结点不止有一个后继指针 next 指向后面的结点,还有一个前驱指针 prev 指向前面的结点。
- 双向链表可以支持 O(1) 时间复杂度的情况下找到前驱结点,正是这样的特
点,也使双向链表在某些情况下的插入、删除等操作都要比单链表简单、高效; - LinkedHashMap就是双向链表的结构;
- 双向循环链表
6.数组与链表
-
时间复杂度
-
数组简单易用,使用的是连续的内存空间,可以借助 CPU 的缓存机制,访问效率更高。而链表在内存中并不是连续存储,所以对 CPU 缓存不友好,没办法有效预读;
-
链表天然支持动态扩容
7.常见的缓存淘汰策略
先进先出策略 FIFO(First In,FirstOut)
最少使用策略 LFU(Least Frequently Used)
最近最少使用策略 LRU(Least Recently Used)
8.链表实现LRU思路
- 维护一个链表,保存要被调用的数据,数量是n。其中,越“早”被使用数据,越排在链表尾部;
- 当有一个数据被使用时,先与链表中保存的数据进行比较
- 如果数据已存在链表,将数据挪到链表头部;
- 如果数据不存在链表,看链表是否已满,如果已满,对链表尾部进行删除,如果未满,插入链表头部;
- 不管链表是否满了,都需要遍历与链表中的原数据进行比较,所以时间复杂度是O(n)
9.编写链表代码注意点
- 如果链表为空时,代码是否能正常工作?
- 如果链表只包含一个结点时,代码是否能正常工作?
- 如果链表只包含两个结点时,代码是否能正常工作?
- 代码逻辑在处理头结点和尾结点的时候,是否能正常工作?
10.数组、链表代码练习(待续)
- 单链表判断回文字
- 单链表反转
- 链表中环的检测
- 两个有序的链表合并
- 删除链表倒数第 n 个结点
- 求链表的中间结点