06链表上:如何实现LRU缓存淘汰算法?
1 链表vs数组:
二者的区别首先在插入删除和随机访问的时间复杂度;
数组简单易用,使用连续的空间,可以借助CPU的缓存机制,预读数组中的数据,访问效率高;
而链表在内存中不是连续存储,对CPU缓存不友好,没办法有效预读。
数组的缺点是大小固定,一经声明就要占用整块连续内存空间。如果过大,浪费内存,如果过小,可能不够用。这时只能再申请一个更大的空间,把原数组拷贝进去,非常费时;而链表天然支持动态扩容。
07链表下
1.常见的检查链表代码是否正确的边界条件:
链表为空时是否正确?
链表只包含一个节点时是否正确?
链表只包含两个节点时是否正确?
处理头节点和尾节点时是否正确?
2.链表题目:206,141,21,19,876
单链表反转
链表中环的检测
两个有序的链表合并
删除链表倒数第 n 个结点
求链表的中间结点
08.如何实现浏览器的前进和后退
1.栈的实现:可以用数组,可以用链表
2.栈的应用:
函数调用:
表达式求值:
通过两个栈实现,一个用来保存操作数,另一个用来保存运算符;
从左到右遍历表达式,遇到数字,就直接压入操作数栈,当遇到运算符,就与运算符栈的栈顶元素进行比较;
如果比运算符栈顶元素的优先级高,就将当前运算符压入运算符栈,如果比运算符栈顶元素的优先级低或者相同,从运算符栈中取栈顶运算符,从操作数栈的栈顶取2个操作数,然后进行计算,再把计算完的结果压入操作数栈,继续进行和运算符栈顶元素的比较;
如下图:
括号匹配:
浏览器的前进和后退:
用两个栈实现,X和Y,首次浏览的页面一次压入栈X,当点击后退按钮时,再依次从栈X中出栈,并将出栈的数据依次放入栈Y中。
当点击前进按钮时,再依次从栈Y中取出数据,放入栈X中。
当栈X中没有数据时,就说明没有页面可以继续后退浏览了。
当栈Y中没有数据时,就说明没有页面可以继续前进浏览了。
注:栈X中的栈顶元素是当前浏览的页面。
09.队列:队列在线程池等有限资源池中的应用
循环队列:
阻塞队列:
在队列的基础上增加了阻塞操作。
当队列为空,从队头读数据会被阻塞,直到队列中有数据;
当队列已满,从队尾插入数据会被阻塞,直到队列中有空位置。
阻塞队列可用于实现生产者消费者模型。
但是当有多个线程同时操作时,会存在线程安全问题。
并发队列:
线程安全的队列叫做并发队列。最简单的实现方式是入队和出队的时候加锁。
问题:当线程池没有空闲线程,新的任务请求线程资源时,线程池如何处理呢?
方法1:非阻塞的处理方式,直接拒绝任务请求;
方法2:阻塞的处理方式,将请求排队,当有空闲的线程时,取出排队的请求继续处理。那么怎样存储排队的请求呢?
有两种方式:
1.基于链表的实现方式,实现一个支持无限排队的无界队列,但可能导致过多的请求排队等待,请求处理的响应时间过长。
2.基于数组实现的有界队列,超过队列大小时,拒绝接下来的请求。