所谓链表也是以种线性结构,相比与数组,比数组稍微有点复杂,
1>提出问题,数组与链表都是线性表,那2者的区别有哪些呢?适用场景又有什么不同呢?
a>数组内部需要一套连续的内存空间,来存放我们的数据,而链表不需要连续的内存空间,而是以一块一块的内存块来储存空间,需要指针来指引.
图数组与链表的内存分布:方法
b>就增加删除而言:数组为达到连续的存储空间,需要一定大量的元素,时间复杂度为O(n),增加删除只是限定在数组(0,length-2)区间,且在数组的末尾增加删除元素的时间复杂度为O(1),但是基于链表增加和删除一个元素只需要改变指针的指引方向即可,时间复杂度为O(1),针对链表的查询操作而言,时间复杂度为O(n),需要遍历链表结构,针对存储空间而言,链表需要更大的存储空间,需要存放指针.
总结,数组与链表的对比。就插入,插入,删除时间复杂度的对比
数组 | 链表 | |
插入,删除操作 | O(n) | O(1) |
查询操作 | O(1) | O(n) |
就使用场景来说,为考虑到应用需要大量的增加删除而言,基于链表性能更高效,基于频繁的查询操作,基于数组更加高效,总之数据支持随机访问,基于链表的增加删除操作,用到的思想是空间换时间,因为基于链表需要有存放指针的空间
2>提出问题:基于链表结构,有哪几种链表,他们的区别是什么
a>链表分为单链表,循环链表,双向链表,循环双向链表,
a.1:单链表
说明:这是一张典型的的单链表结构图:从图中可以看出,没一个节点都有数据和执行下一个节点的数据指针next针对,所以相比数组而言,需要额外的内存空间存放next指针,其中要注意的2个地方是头结点执行的是数据,记录的是链表的基地址.尾节点指向的是空地址null.
就增加删除元素而言,我们来看一张单链表删除增加的结构图就一目了然。
总结单链表的增加删除, 图中很明显的看出,就单链表的增加删除,只需要改变各个指针的指向就可以了.时间复杂度为O(1)
就查找元素而言,需要通过单链表的首地址一次遍历整张链表,直到找到该元素即可,当所找的元素位于链表的头,则时间复杂度为O(1),位于其他地方,总结来说,时间复杂度为O(n)
a,2>循环链表:循环链表相比单链表稍微有点复杂,只是在单链表中尾节点指向的不是null,而是指向了头节点,唯一的优点是链表可以循环的遍历,比如从尾节点可以遍历到链表的头结点.
a.3>双向链表:
说明:从图中可以看出,双向链表需要更多册存储空间,一个是prev一个是next,其中next指向下一个节点,prev指向前驱节点,基于这种结构,双向链表支持双向遍历,双向链表可支持时间复杂度为O(1)的去寻找前驱节点数据,也是空间换时间的思想.
a.4>双向循环链表:这里不做解说:
针对链表和数组再次总结使用场景:针对需要频繁的增加删除的场景而言,链表更加合适,但是链表需要更多的内存空间,对于链表他更适合频繁的增加,删除操作,但是频繁的增加删除操作,链表需要频繁的申请内存空间和释放,容易导致内存碎片,这也是我们应该要考虑点.
3>解答开篇,如何实现基于链表的lru缓存淘汰算法,
lru缓存淘汰算法:该算法师根据数据的历史访问记录来进行淘汰数据,思想,如果该数据最近倍访问过,那么将来的访问率也比较高
实现思想:如果该数据已经存在我们的链表中,经过一次遍历,找到到该元素,需要将该数据放到链表的头部
如果该数据不在链表中,此时链表有足够的空间,将该数据放到链表的头部即可,如果该链表的数据已经满了,则删除链表尾的数据.,这腾出存储空间,将该数据放到链表的头部,总之lru核心思想是将最近最少使用的数据放到链表的尾部,如果存储空间不足的时候,淘汰即可,链表的头部总是存放最近使用率最频繁的数据.