缓存策略
1,FIFO(first in first out)先进先出
2,LFU(least frequently used)最少使用策略
3,LRU(least recently used)最近最少使用策略
数组链表区别
数组
内存连续
链表
内存不连续
链表分类
单链表
双向链表
循环链表
双向循环链表
执行较慢的程序
可以通过空间换时间来进行优化
消耗过多内存的程序,可以通过时间换空间来降低内存的消耗
链表数据使用
数据缺点大小固定,一经声明就要占用整块的连续内存空间
java中ArrayList使用动态扩容,但扩容时把原数组拷贝到扩充后的数组中是非常耗时的
如果对于代码对内存使用非常苛刻就需要使用数组,因为链表每个结点指针都会占用内存
对链表进行频繁插入、删除会导致频繁内存申请和释放,容易造成内存碎屏,java中也可能导致频繁GC
LRU缓存淘汰算法
基于有序单链表事项LRU算法
维护一个有序单链表,越靠近链表尾部的结点是越早访问的。当有一个新的数据被访问是,我们从链表头开始顺序遍历链表
1,如果此数据之前已经被缓存在链表中,我们遍历得到这个数据对应的结点,并将其从原来的位置删除,然后在插入到链表的头部
2,如果此数据没有在缓存链表中,分为两种情况
- 如果此时缓存未满,则将此结点直接插入到链表头部
- 如果此时缓存满了,则链表尾结点删除,将新数据结点插入到链表的头部
写链表
写链表技巧
1,理解指针或引用的含义
将某个变量赋值给指针,实际上就是将这个变量的地址赋值给指针,或者返过来说,指针中存储了这个变量的内存地址,指向了这个变量,通过指针就能找到这个变量
2,警惕指针丢失和内存泄露
3,利用哨兵简化实现难度
带头结点链表
//在数组a中,查找key,返回key所在的位置
int find(char* a,int n,char key){
if (a == NULL || n <= 0){
return -1;
}
int i =0;
while (i < n){
if (a[i] == key){
return i;
}
++i;
}
return -1;
}
//在数组a中,查找key,返回key所在的位置
int find2(char* a,int n,char key){
if (a == NULL || n <= 0){
return -1;
}
if (a[n -1] == key){
return n -1;
}
char tmp = a[n-1];
a[n-1] = key;
int i =0;
while (a[i] != key){
++i;
}
a[n-1] = tmp;
if (i == n-1){
return -1;
} else {
return i;
}
}
对比两段代码,在字符a很长时,第二段代码更快,我们通过一个哨兵a[n-1]=key,成功省略掉了一个比较语句
4,重点留意边界条件处理
- 如果链表为空时,代码是否能正常工作
- 如果链表只包含一个结点,代码是否能正常工作
- 如果链表只包含两个结点,代码是否能正常工作
- 代码逻辑在处理头结点和尾结点时,是否能正常工作
5,举例画图,辅助思考
6,多练多写没有捷径
创建链表头插法,尾插法
LinkedList createListByHead(){
int arr[] = {4,5,6,7,8,89,9,2,2,34};
LinkedList head = malloc(sizeof(Node));
head->next =NULL;
for(int i = 0;i < 10;i++){
Node *p = malloc(sizeof(Node));
p->value=arr[i];
p->next = head->next;
head->next = p;
}
return head;
}
LinkedList createListByTail(){
int arr[] = {4,5,6,7,8,89,9,2,2,34};
LinkedList head = malloc(sizeof(Node));
head->next =NULL;
LinkedList p = head;
for(int i = 0;i < 10;i++){
Node *q = malloc(sizeof(Node));
q->value=arr[i];
q->next=NULL;
p->next = q;
p=p->next;
}
return head;
}
单链表反转
//不带头结点单链表反转
LinkedList reverseList(Node* head){
if (head == NULL){
return NULL;
}
if (head->next == NULL){
return head;
}
Node *root = head;
Node *pre = NULL;
Node *next = NULL;
while (root->next!= NULL){
next=root->next;
root->next=pre;
pre=root;
root=next;
}
root->next=pre;
return next;
}
链表中环检测
int isCircle(Node* head){
Node* fast=head;
Node* slow=head;
while (fast != NULL && slow != NULL){
fast = fast->next;
slow = slow->next;
if (fast != NULL){
fast = fast->next;
}
if (slow != NULL && slow == fast){
return 1;
}
}
return 0;
}
两个有序链表合并
LinkedList mergeList(Node* first,Node* second){
if (first ==NULL && second ==NULL){
return NULL;
} else if (first ==NULL){
return second;
} else if (second ==NULL){
return first;
}
Node* p = NULL;
Node* head = NULL;
while (first!=NULL && second!=NULL){
if (first->value <= second->value){
if (head == NULL){
head = first;
p = first;
} else {
p->next = first;
p = p->next;
}
first = first->next;
} else {
if (head == NULL){
head = second;
p = second;
} else {
p->next = second;
p = p->next;
}
second = second->next;
}
}
if (first!=NULL){
p->next = first;
}
if (second!=NULL){
p->next = second;
}
return head;
}
删除链表倒数第n个结点
LinkedList deleteElementFromTail(Node* head,int i){
if (head==NULL || i < 0){
return NULL;
}
Node* fast = head;
Node* low = head;
Node* pre = NULL;
while (i -1 >0){
if (fast->next != NULL){
fast = fast->next;
} else {
return head;
}
i--;
}
while (fast->next != NULL){
fast = fast->next;
pre = low;
low = low->next;
}
if (low == head){
head = head->next;
free(pre);
} else {
pre->next = pre->next->next;
free(low);
}
return head;
}
求链表的中间结点
LinkedList findMiddleOfList(Node* head){
if (head == NULL || head->next == NULL || head->next->next ==NULL){
return head;
}
Node* fast = head;
Node* low = head;
while (fast->next !=NULL){
fast = fast->next->next;
low = low->next;
}
return low;
}