公司有网站域名 如何做网站网站统计工具有哪些
文章目录
- 链表结构
- 单链表
- 循环链表
- 双向链表
- 删除操作(双向链表的优点)
- 链表 VS 数组性能大比拼
- LRU 缓存淘汰算法
- 课后思考
链表结构
缓存大小有限,当缓存用满时,哪些数据应该被清理出去,哪些数据应该被保留?这就需要缓存淘汰策略来决定。常见的策略有三种:先进先出策略 FIFO(First In,First Out)、最少使用策略 LFU(Least Frequently Used)、最近最少使用策略 LRU(Least Recently Used)。
链表并不需要一块连续的内存空间,它通过“指针”将一组零散的内存块串联起来使用。
单链表
c++ 里面的forward_list?
把内存块称为链表的“结点”,为了将所有的结点串起来,每个链表的结点除了存储数据之外,还需要记录链上的下一个结点的地址。如上图所示,我们把这个记录下个结点地址的指针叫作后继指针 next。
第一个结点叫作头结点,最后一个结点叫作尾结点。头结点用来记录链表的基地址。有了它,我们就可以遍历得到整条链表。而尾结点特殊的地方是:指针不是指向下一个结点,而是指向一个空地址 NULL,表示这是链表上最后一个结点。
数组的插入、删除操作时,为了保持内存数据的连续性,需要做大量的数据搬移,所以时间复杂度是 O(n)。而链表则不用保持内存的连续性,因为链表的存储空间本身就不连续。所以,在链表中插入和删除一个数据是非常快速:
但是链表要想随机访问第 k 个元素,就没有数组那么高效,因为内存地址不连续,无法进行寻址操作。而是需要根据指针一个结点一个结点地依次遍历,直到找到相应的结点,时间复杂度为 O(n)。
循环链表
跟单链表唯一的区别就在尾结点,循环链表的尾结点指针是指向链表的头结点,首尾相连。
循环链表的优点是从链尾到链头比较方便,当要处理的数据具有环型结构特点时,就特别适合采用循环链表。
双向链表
c++ 里面的list?
而双向链表,顾名思义,支持两个方向,每个结点不止有一个后继指针next
指向后面的结点,还有一个前驱指针prev
指向前面的结点。
结构上来看,双向链表可以支持 O(1) 时间复杂度的情况下找到前驱结点,正是这样的特点,也使双向链表在某些情况下的插入、删除等操作都要比单链表简单、高效。
删除操作(双向链表的优点)
从链表中删除一个数据无外乎这两种情况:
- 删除结点中“值等于某个给定值”的结点
- 删除给定指针指向的结点。
第一种情况,不管是单链表还是双向链表,为了查找到值等于给定值的结点,都需要头结点开始依次遍历对比,直到找到值等于给定值的结点,然后将其删除。单纯的删除操作时间复杂度是 O(1),但遍历查找的时间是主要的耗时点,对应的时间复杂度为 O(n)。
第二种情况,如果已经找到了要删除的结点,但是删除某个结点 q 需要知道其前驱结点,而单链表并不支持直接获取前驱结点,所以,为了找到前驱结点,还是要从头结点开始遍历链表,直到 p->next=q,说明 p 是 q 的前驱结点。但对于双向链表来说,双向链表中的结点已经保存了前驱结点的指针,不需要像单链表那样遍历。单链表删除操作需要 O(n) 的时间复杂度,而双向链表只需要在 O(1) 的时间复杂度!
除了插入、删除操作有优势之外,对于一个有序链表,双向链表的按值查询的效率也要比单链表高一些。因为可以记录上次查找的位置 p,每次查询时,根据要查找的值与 p 的大小关系,决定是往前还是往后查找,所以平均只需要查找一半的数据。
链表 VS 数组性能大比拼
LRU 缓存淘汰算法
思路:我们维护一个有序单链表,越靠近链表尾部的结点是越早之前访问的。当有一个新的数据被访问时,我们从链表头开始顺序遍历链表:
1.如果此数据之前已经被缓存在链表中了,我们遍历得到这个数据对应的结点,并将其从原来的位置删除,然后再插入到链表的头部。
2. 如果此数据没有在缓存链表中,又可以分为两种情况:
- 如果此时缓存未满,则将此结点直接插入到链表的头部;
- 如果此时缓存已满,则链表尾结点删除,将新的数据结点插入链表的头部。
课后思考
如何判断一个字符串是否是回文字符串的问题,我想你应该听过,我们今天的题目就是基于这个问题的改造版本。如果字符串是通过单链表来存储的,那该如何来判断是一个回文串呢?你有什么好的解决思路呢?相应的时间空间复杂度又是多少呢?
如何判断一个字符串是否是回文字符串?
常规判断很简单,贴一个c++代码:
#include <iostream>
#include <string>
#include <iterator>using namespace std;int main(){cout << "请输入一个字符串:" << endl;string str;while (cin >> str){bool flag = true;auto it1 = str.begin();auto it2 = str.end()-1;auto mid = it1 + str.size() / 2;while (it1 != mid){if (*it1 != *it2){flag = false;break;}++it1; --it2;}cout << str << (flag ? "是" : "不是") << "回文字符串" << endl;}return 0;
}
如果字符串是通过单链表来存储的,那该如何来判断是一个回文串呢?