链表:
1.def:数组内存连续,申请过大会失败;链表内存零散
2.链表结构:
(1)单链表:
插入、删除O(1);
访问不能像数组根据首地址和下标直接寻址,需要依次遍历,O(n)
(2)循环链表:特殊单链表,适合环型结构数据(约瑟夫问题)
(3)双向链表:
【区别】
*和其他比占用更多内存空间,但双向遍历更灵活
*——双向链表某些情况的插入删除更高效:
删除值:相同,O(n)(遍历查找主要耗时);
删除值指针指向结点:普通需要遍历找前驱,O(n);双向链表O(1),插入同理。
*对于有序链表,双向链表按值查询可记录位置决定以后查找方向,效率更高(查一半)
【引申】“空间换时间”设计思想——缓存
(4)双向循环链表
3.链表&数组对比:
(1)对于CPU缓存,数组连续空间更友好,能有效预读,访问效率更高
(2)数组大小固定声明过大可能内存不足分配失败,声明过小可能不够用,拷贝费时;链表天然支持动态扩容
(3)如果代码对内存使用非常苛刻数组更适合;链表内存消耗翻倍且频繁插入删除导致频繁内存申请释放容易造成内存碎片
【思考】如何实现基于链表的LRU缓存淘汰算法?
有序单链表越尾部结点是越早访问的,新数据被访问时,从表头顺序遍历:
如果数据缓存在表中,从原位删除,插入表头;如果不在:
缓存未满——插入头部;缓存已满——删除尾结点,新结点插入头部
复杂度:遍历O(n)
优化:散列表O(1)
如何写链表代码:
1.警惕指针丢失&内存泄漏
2.哨兵简化
3.边界条件处理
常见:206单链表反转,141链表中环的检测,21两个有序链表合并,19删除链表倒数第n个节点,876求链表中间节点