LRU,即最近最少使用算法,是一种常用的页面置换算法。
实现方式有很多,比如数组实现,为每个元素设置时间。或者移动数组中的元素等。移动数组中元素的方式在物理页面数较多时,会移动量很大,效率很低。
也可以使用单链表来实现。
我尝试过采用链表队列来实现,但涉及到非队首数据的删除,有违队列的限制,所以放弃。
于是采用单链表来实现。其中涉及到查找数据。由于单链表删除不太方便,所以查找返回数据的位置,再根据位置进行删除。这样的话需要查找一次,再按位置删除一次,影响效率。
这时应该会有小伙伴想到双向链表,查找并返回数据结点,然后删除,这样就可以直接删除,不需要额外再进行一次查找删除,诶 效率更高了。
java中可以使用hashmap + 双向链表来实现,效率就又高了。hashmap经过散列可以在O(1)时间复杂度找到相关元素。
java中的LinkedHashMap 就是采用hashmap + 双向链表实现的,所以LinkedHashMap天生支持LRU,可以直接使用LinkedHashMap。
下面是我采用单链表方式的实现,实现比较复杂,练练手。
#include <iostream>
#include <stdio.h>
//测试页面走向 7 0 1 2 0 3 0 4 2 7 1
//测试页面走向 0 1 2 3 0 1 4 0 1 2 3 4
//测试页面走向 3 2 1 0 3 2 4 3 2 1 0 4
//测试页面走向 1 2 3 4 1 2 5 1 2 3 4 5
//测试页面走向 1 2 1 3 1 2 4 2 1 3 4 5
//测试页面走向 1 2 3 6 4 7 3 2 1 4 7 5 6 5 2 1
typedef int DataType;
//单链表结点类型
typedef struct node{
DataType data;
struct node * next;
}ListNode;
typedef ListNode * LinkList;
//带头结点尾插法建表(创建一个空表)
LinkList createLinkList(){
LinkList head = (ListNode *)malloc(sizeof(ListNode));
head->next = NULL;
return head;
}
//按结点值查找(如果找到,则返回该结点的位置,否则返回0)
int LocateElem(LinkList head,DataType e)
{
int i=0;
LinkList p=head->next;
while(p)
{
i++;
if(p->data==e) /* 找到这样的数据元素 */
return i;
p=p->next;
}
return 0;
}
//按位置插入
void insertList(LinkList head,int i,DataType x){
ListNode *p ,*s;
int j;
p=head;j=0;
while (p!=NULL && j<i-1) {
p=p->next;
++j;
}
if (p==NULL) {
printf("error\n");
return;
} else {
s = (ListNode *)malloc(sizeof(ListNode));
s->data = x;
s->next = p->next;
p->next = s;
}
}
//按位置删除
DataType deleteList(LinkList head,int i){
ListNode *p,*s;
DataType x;
int j;
p = head;
j = 0;
while (p!=NULL && j<i-1) {
p=p->next;
++j;
}
if (p==NULL) {
printf("位置错误\n");
exit(0);
} else {
s = p->next;
p->next = s->next;
x = s->data;
free(s);
return x;
}
}
//初始化队列
void initializeList(int list[],int number){
for (int i = 0; i < number; i ++) {
list[i] = -1;
}
}
//展示队列状态
void showList(int list[], int number){
printf("队列状态:");
for (int i = 0; i < number; i ++) {
printf("%2d",list[i]);
}
printf("\n");
}
//展示链表状态
void showLinkList(LinkList head){
ListNode *p = head->next;
while (p) {
printf("%2d",p->data);
p = p->next;
}
printf("\n");
}
//展示缺页置换等信息
void informationCount(int missingCount,int replaceCount,int pageNum){
printf("缺页次数:%d 缺页率:%d/%d\n",missingCount,missingCount,pageNum);
double result = (double)(pageNum - missingCount)/(double)pageNum;
printf("置换次数:%d 命中率:%.2f\n",replaceCount,result);
}
//最近最久未使用置换算法
void replacePageByLRU(LinkList head ,int phyNum,int strList[],int pageNum){
//置换次数
int replaceCount = 0;
//缺页次数
int missingCount = 0;
//链表中元素的数量
int phyCount = 0;
for (int i = 0; i < pageNum; i ++) {
//当页面数超过3个时,首先查找要访问的页面是否在队列中,
//如果不在队列中,则出队,并将该页面号入队。
//如果在队列中,删除该结点,并将该结点重新入队。
if (phyCount < phyNum) {
int position = LocateElem(head, strList[i]);
if (!position) {
//没找到
insertList(head, phyCount+1, strList[i]);
missingCount++;
phyCount++;
} else {
//找到了
deleteList(head, position);
insertList(head, phyCount, strList[i]);
}
} else {
int l = LocateElem(head, strList[i]);
if (!l) {
//没找到,删除头部,插入尾部
deleteList(head, 1);
insertList(head, phyNum, strList[i]);
missingCount++;
replaceCount++;
} else {
//找到了,删除该结点,插入尾部
deleteList(head, l);
insertList(head, phyNum, strList[i]);
}
}
//展示当前链表状态
showLinkList(head);
}
informationCount(missingCount, replaceCount, pageNum);
}
int main(int argc, const char * argv[]) {
//物理块的数量
int phyBlockNum;
printf("请输入物理块数量:\n");
scanf("%d",&phyBlockNum);
//页面数量
int pageNum;
printf("请输入要访问的页面总数:\n");
scanf("%d",&pageNum);
//保存页面号引用串
int pageNumStrList[pageNum];
printf("请输入要访问的页面号:\n");
for (int i = 0; i < pageNum; i ++) {
scanf("%d",&pageNumStrList[i]);
}
//定义一个链表
LinkList head = createLinkList();
showList(pageNumStrList, pageNum);
//lru置换算法
replacePageByLRU(head, phyBlockNum, pageNumStrList, pageNum);
return 0;
}