LRU
Least Recently Used
最近最久未使用
LRU
的设计原则是:如果一个数据在最近一段时间没有被访问到,那么在将来它被访问的可能性也很小。
实现LRU
- 用数组来存储数据,给每一个数据项标记一个访问时间戳,每次插入新数据项的时候,先把数组中存在的数据项的时间戳自增,并将新数据项的时间戳置为
0
并插入到数组中。每次访问数组中的数据项的时候,讲被访问的数据项的时间戳置为0
。当数组空间已满时,将时间戳最大的数据项淘汰。简单实现:
//
// main.c
// lru
//
// Created by 李龙 on 2018/12/10.
// Copyright © 2018 李龙. All rights reserved.
//
#include <stdio.h>
#include <stdlib.h>
#define MAXSIZE 10
#define ERROR 0
#define SUCCESS 1
//结点数据
typedef struct {
int data; //数据
int num; //时间戳
} Node;
//缓存数据
typedef struct {
struct Node *caches[MAXSIZE]; //缓存数组
int length; //当前长度
} *Cache, Caches;
//判断是否有缓存
int hasCache(Cache cache, int data) {
if (cache->length == 0) {
return ERROR;
}
for (int i = 0; i < cache->length; i++) {
Node *node = (Node *)cache->caches[i];
if (node->data == data) {
//时间戳置为0
node->num = 0;
return SUCCESS;
}
}
return ERROR;
}
//添加缓存
void addCache(Cache cache, int data) {
int i;
//所有时间戳+1
for (i = 0 ; i < cache->length; i++) {
Node *node = (Node *)cache->caches[i];
node->num++;
}
//有缓存则将该数据时间戳置为0, 已在hasCache中置为0了
if (hasCache(cache, data) == SUCCESS) {
//没有缓存
} else {
//缓存数没有最大,则进行创建结点加入数组, 时间戳为0
if (cache->length < MAXSIZE) {
Node *node = (Node *)malloc(sizeof(Node));
node->data = data;
node->num = 0;
//数组长度+1
cache->caches[cache->length] = (struct Node *)node;
cache->length++;
//若为最大值, 则找到时间戳最大的, 数据替换, 时间戳置为0
} else {
int j = 0, max = 0;
for (i = 0; i < cache->length; i++) {
Node *node = (Node *)cache->caches[i];
if (node->num > max) {
j = i;
max = node->num;
}
}
Node *node = (Node *)cache->caches[j];
node->data = data;
node->num = 0;
}
}
}
int main(int argc, const char * argv[]) {
Cache cache = (Cache)malloc(sizeof(Caches));
cache->length = 0;
int i, data;
while (1) {
printf("请输入访问的数据:\n");
scanf("%d", &data);
addCache(cache, data);
printf("缓存的数据数据为:\n");
for (i = 0; i < cache->length; i++) {
Node *node = (Node *)cache->caches[i];
printf("数据: %d\n", node->data);
printf("时间戳: %d\n", node->num);
}
}
return 0;
}
- 利用一个链表来实现,每次新插入数据时将新数据插到链表的头部:每次缓存命中(即数据被访问),则将数据移到链表头部;当链表满时,将链表尾部的数据丢弃。简单实现:
//
// main.c
// lru2
//
// Created by 李龙 on 2018/12/10.
// Copyright © 2018 李龙. All rights reserved.
//
#include <stdio.h>
#include <stdlib.h>
#define MAXSIZE 10
#define ERROR 0
#define SUCCESS 1
//结点
typedef struct Node {
int data; //数据
struct Node *next; //next指针
} Node;
//缓存数据
typedef struct {
Node *node; //头结点
int length; //长度
} Cache;
//判断是否有缓存, 若有缓存,将这个结点置到链头
int hasCache(Cache *cache, int data) {
if (cache->length <= 0) {
return ERROR;
}
//如果是首结点,则直接返回,不做任何操作
if (cache->node->data == data) {
return SUCCESS;
}
Node *node = cache->node;
//记录前一个结点
Node *pre = node;
while (node) {
if (node->data == data) {
//前一个结点的next指向现结点的next
pre->next = node->next;
//记录当前头结点
pre = cache->node;
//将当前结点置为头结点
node->next = pre;
cache->node = node;
return SUCCESS;
} else {
//记录前一个结点,指向下一个结点
pre = node;
node = node->next;
}
}
return ERROR;
}
//添加缓存
void addCache(Cache *cache, int data) {
//若有缓存,则将这个结点置为链头
if (hasCache(cache, data) == SUCCESS) {
//若没有缓存,添加进缓存
} else {
//创建结点,将结点置为链头
Node *node = (Node *)malloc(sizeof(node));
node->data = data;
node->next = cache->node;
cache->node = node;
//若缓存没满,则length+1
if (cache->length < MAXSIZE) {
cache->length++;
//若缓存满了,将最后一个结点删除
} else {
node = cache->node;
for (int i = 0; i < MAXSIZE - 1; i++) {
node = node->next;
}
node->next = NULL;
}
}
}
int main(int argc, const char * argv[]) {
Cache *cache = (Cache *)malloc(sizeof(Cache));
cache->length = 0;
int i, data;
while (1) {
printf("请输入访问的数据:\n");
scanf("%d", &data);
addCache(cache, data);
printf("缓存的数据数据为:\n");
Node *node = cache->node;
for (i = 0; i < cache->length; i++) {
printf("数据: %d\n", node->data);
node = node->next;
}
}
return 0;
}
- 利用双向链表和
hashmap
来实现。访问数据时,通过哈希函数算出哈希值(注意解决冲突),从数组中取值,若没有,进行创建,添加进数组,并设置它为头结点。若存在,将它置为头结点。若缓存达到上限,删除记录的尾结点。使用哈希使其取值简单。简单实现:
//
// main.c
// lru3
//
// Created by 李龙 on 2018/12/11.
// Copyright © 2018 李龙. All rights reserved.
//
#include <stdio.h>
#include <stdlib.h>
#define MAXSIZE 10
//结点
typedef struct {
int data; //结点
int pre; //前一个数据下标
int next; //后一个数据下标
} Node;
//缓存
typedef struct {
Node *caches[MAXSIZE * 2]; //哈希数组
int start; //头结点
int end; //尾结点
int length; //长度
} Cache;
//初始化缓存列表
Cache * creatCaches() {
Cache *cache = (Cache *)malloc(sizeof(Cache));
cache->length = 0;
//设置首尾结点尾-1
cache->start = -1;
cache->end = -1;
for (int i = 0; i < MAXSIZE * 2; i++) {
cache->caches[i] = NULL;
}
return cache;
}
//算取哈希值,使用除余法
int hashData(Cache *hashCache, int data, int *indexNode) {
int index = data % (MAXSIZE * 2);
//判断该位置是否有值
while (hashCache->caches[index] != NULL) {
//有值
Node *node = hashCache->caches[index];
//若跟值相同,则进行记录这个位置,并返回-1标记为有值
if (node->data == data) {
*indexNode = index;
return -1;
}
//若不相同,则冲突处理
index++;
index = index % (MAXSIZE * 2);
}
return index;
}
//删除结点
void deleteHashCache(Cache *hashCache) {
//获取尾结点坐标
int end = hashCache->end;
//尾结点
Node *node = hashCache->caches[end];
//尾结点前一个结点坐标
int pre = node->pre;
node = hashCache->caches[pre];
//置为-1
node->next = -1;
//尾结点
hashCache->end = pre;
//删除结点, 释放指针
free(node);
}
//添加缓存
void addHashCache(Cache *hashCache, int data) {
//记录相同值位置
int indexNode;
//取哈希值
int index = hashData(hashCache, data, &indexNode);
//若有相同值
if (index == -1) {
Node *node = hashCache->caches[indexNode];
//设置该点为头结点,若为尾结点,则将尾结点前一个结点置为缓存尾结点
if (indexNode == hashCache->end && hashCache->length != 1) {
hashCache->end = node->pre;
}
//若不为头结点,将它前一个结点的next指向它的next
if (node->pre != -1) {
Node *preNode = hashCache->caches[node->pre];
preNode->next = node->next;
}
//若不为尾结点,将它后一个结点的pre指向它的pre
if (node->next != -1) {
Node *nextNode = hashCache->caches[node->next];
nextNode->pre = node->pre;
}
//拿到头结点,将头结点pre指向它
Node *startNode = hashCache->caches[hashCache->start];
startNode->pre = indexNode;
//它的pre指向-1
node->pre = -1;
//若它不为头结点,将它的next指向头结点
if (indexNode != hashCache->start) {
node->next = hashCache->start;
}
//设置它为头结点
hashCache->start = indexNode;
//只有一个结点,置next为-1
if (hashCache->length == 1) {
node->next = -1;
}
//若无缓存值
} else {
//没有到最大缓存数,长度+1
if (hashCache->length < MAXSIZE) {
hashCache->length++;
} else {
//删除尾结点
deleteHashCache(hashCache);
}
//创建新结点
Node *node = (Node *)malloc(sizeof(Node));
node->data = data;
//头结点pre为-1
node->pre = -1;
node->next = hashCache->start;
hashCache->caches[index] = node;
//若有值
if (hashCache->start != -1) {
//头结点的pre置为它
Node *startNode = hashCache->caches[hashCache->start];
startNode->pre = index;
}
//记录头结点
hashCache->start = index;
//记录尾结点
if (hashCache->length == 1) {
hashCache->end = index;
}
}
}
int main() {
Cache *hashCache = creatCaches();
int i, data;
while (1) {
printf("请输入访问的数据:\n");
scanf("%d", &data);
addHashCache(hashCache, data);
printf("缓存的数据数据为:\n");
int index = hashCache->start;
printf("首结点坐标为:%d\n", hashCache->start);
for (i = 0; i < hashCache->length; i++) {
Node *node = hashCache->caches[index];
printf("数据为:%d 上一个坐标为:%d 下一个坐标为:%d\n", node->data, node->pre, node->next);
index = node->next;
}
printf("尾结点坐标为:%d\n", hashCache->end);
}
return 0;
}
实现相对来说有点复杂,而且可能还会有bug
,但实现思路大概就是这个样子。
第三种方法是现阶段,一般使用的LRU
算法。