1.缓存是什么?
- 缓存是提高数据读取性能的技术,在硬件和软件开发中有着非常大的应用。当缓存大小有限的时候,哪些数据应该保留这就需要缓存淘汰策略来决定。
- 常见的策略
1.先进先出的策略
2.最少使用策略(LFU)
3.最近最少使用策略(LRU) - 缓存是用空间换时间的例子。在硬盘的数据每次都会询问一次硬盘会比较慢。实现将数据加载在内存中,虽然会耗费内存空间但是每次查询的速度就会大大提高。
2. 数组与链表
- 数组一旦声明就会占用连续内存空间。如果声明的数组出现不够用的时候,只能去申请一个更大的空间,将原数组拷贝进去。
- 链表本身没有大小的限制,天然地支持动态扩容。
- 数组简单易用,在实现上使用的是连续的内存空间,可以借助cpu的缓存机制,预读数组中的数据,所以访问效率更高。然而链表在内存中并不是连续存储,对cpu缓存不友好,没办法有效预读。
循环链表和双向链表
*循环链表和单链表的区别在于尾节点,单链表的尾节点指向空地址。循环链表比较适合处理环形结构的数据。
*双向链表:需要两个空间来存储后继结点和前驱结点的地址。如果存储相同多的数据,双向链表比单链表占用更多的内存空间。
3.基于链表实现LRU缓存淘汰算法?
思路:维护一个有序单链表,靠近链表尾部的节点是越早之前访问的。当有一个新数据被访问时,我们从链表头开始遍历链表。
1.如果此数据之前已经被缓存在链表里面了,我们遍历这个数据对应的节点并将其从原来的位置删除,然后插入到链表的头部。
2.如果此数据没有在缓存链中,又可以分为以下两种情况:
- 如果此时缓存没有满,则将此节点直接插入到链表的头部
- 如果此时缓存已满,册从链表为波删除节点,将新的数据节点插入到链表的头部。
4.链表类代码的技巧
- 理解指针和引用的含义:指针中存储了这个变量的内存地址,指向了这个变量,通过指针就能找到这个变量。
- 警惕指针丢失和内存泄露:对于c语言而言,删除链表节点的时候,一定要记得手动释放内存空间。
- 利用哨兵简化实现难度:针对链表的插入、删除操作,需要对插入第一个结点和删除最后一个结点的情况进行特殊里。有哨兵结点的链表叫做带头链表。没有哨兵结点的链表叫作不带头链表。
- 重点留意边界条件处理
- 链表为空,代码是否能正常工作 2.链表只有一个结点,代码能否正常工作
3.链表只有两个结点是,代码能否正常工作
4.代码逻辑在处理头结点和尾节点的时候,是否能正常工作。
//递归方式实现链表反转
Node * reverseList(List head)
{
//如果链表为空或者链表中只有一个元素
if(head == NULL || head->next == NULL)
{
return head;
}
else
{
//先反转后面的链表,走到链表的末端结点
Node *newhead = reverseList(head->next);
//再将当前节点设置为后面节点的后续节点
head->next->next = head;
head->next = NULL;
return newhead;
}
}