页面置换算法(FIFO、LRU、LFU)c++实现

1. FIFO(First in First out) -- 先进先出

先来先服务的策略。如果一个数据最先进入缓存中,则应该最早淘汰掉。也就是说,当缓存满的时候,应当把最先进入缓存的数据给淘汰掉。(这个可以类比队列较好理解)

实现:核心:双向链表实现队列+ hashmap

利用一个双向链表保存数据,当来了新的数据之后便添加到链表末尾,如果Cache存满数据,则把链表头部数据删除,然后把新的数据添加到链表末尾,这样插入和删除都可以在O(1)时间内完成。在访问数据的时候,如果在Cache中存在该数据的话,则返回对应的value值;否则返回-1。访问效率为O(n),可以通过添加hashmap来提高访问速度达到O(logn)。

#include"head.h"//头文件在最后处
#include<math.h>

class FIFOCache
{
public:

	FIFOCache(int capacity)
	{
		FIFOcapacity = capacity;
		FIFOsize = 0;
		list.Listcapcity = capacity;

	}

	int get(int key)
	{
		if (!FIFOmap.count(key))
			return -1;
		Node* node = FIFOmap[key];
		return node->val;
	}
	void put(int key, int value)
	{
		if (FIFOcapacity == 0)
			return;
		if (FIFOmap.count(key))
		{
			Node* node = FIFOmap[key];
			list.remove(node);
			node->val = value;
			list.append(node);
		}
		else
		{
			if (FIFOsize == FIFOcapacity)
			{
				Node* node = list.pop();
				int x = node->val;
				map<int, Node*>::iterator it3 = FIFOmap.find(x);
				//通过find拿到 node.val 为关键字的的迭代器
				FIFOmap.erase(it3);
				--FIFOsize;
			}
			Node* node = new Node(value);

			list.append(node);
			FIFOmap[key] = node;
			++FIFOsize;

		}
	}

	void printFIFO()
	{
		list.printList();
	}
	//数据成员
	int FIFOsize;
	int FIFOcapacity;
	DoubleList list;
	map<int, Node*>FIFOmap;
};

测试代码(FIFO):

    FIFOCache cache(2);
	cache.put(1,1);
	cache.printFIFO();

	cache.put(2, 2);
	cache.printFIFO();

	cout << cache.get(1) << endl;

	cache.put(3, 3);
	cache.printFIFO();

	cout << cache.get(2) << endl;
	cache.printFIFO();

	cache.put(4, 4);
	cache.printFIFO();

	cout << cache.get(1) << endl;

2. LFU(Least Frequently Used) -- 最近最少使用

基于“如果一个数据在最近一段时间内使用次数很少,那么在将来一段时间内被使用的可能性也很小”的思路。

LFU是基于访问次数的。(这里可以把LFU看作是FIFO的优化,数据在原有基础增添一新的数据成员,用以计数数据被访问次数,按照数据被访问计数进行“分组”,计数相同的数据分为一组,淘汰时,选取最计数组,按照FIFO方法淘汰首元素)

实现:核心:存放双向链表的hashmap

LFU可以视为按频率“分组”的FIFO算法,所以我们利用hashmap,key存放数据被访问频率,key对应的value存放key频率下的双向链表。这样淘汰数据时,只要找到最小频率组,再按照FIFO策略淘汰数据即可。同时需要一个hashmap存放,数据地址,加快查找。

访问数据命中,则从相应的双向链表中删除,插入下一频数的双向链表,若该频数双向链表不存在则创建并插入;若删除数据使得双向链表为空,则删除双向链表。

#include"head.h"
#include<math.h>
class LFUNode :public Node//新定义的LFUNode类继承Node类基础上,添加freq数据成员作为计数
{
public:
	LFUNode() {}
	
	LFUNode(int x)
	{
		freq = 0;
		val = x;
	}
	int freq;
};

class LFUcache
{
public:
	LFUcache(int capacity)
	{
		LFUcapacity = capacity;
		LFUsize = 0;
		
	}

	void update(LFUNode*node) //更新节点 每次get或者put 访问频率改变
	{
		int freq = node->freq;
		//删除
		LFUNode* node1 = NULL;
		node1=(LFUNode*)freq_map[freq]->remove(node); ///
		if (freq_map[freq]->Listsize == 0)//如果该节点为最后一个节点,则删除双向链表
		{
			map<int, DoubleList*>::iterator it = freq_map.find(freq);
			freq_map.erase(it);
		}

		//更新
		++freq;
		node1->freq = freq;
		if (freq_map.count(freq))//该频率已存在,则尾部加入
		{
			freq_map[freq]->append(node1);//static_cast<Node*>(node)
		}
		else  //该频率未存在,则创建新双向链表插入
		{
			DoubleList* Dlist = new DoubleList();
			Dlist->append(node1);//
			freq_map[freq] = Dlist;
		}
	}
	int get(int key)
	{
		if (!LFUmap.count(key))
			return -1;
		else
		{
			LFUNode* node = LFUmap[key];
			update(node);
			return node->val;
		}
	}

	void put(int key,int value)
	{
		if (LFUcapacity == 0)
			return;
		if (LFUmap.count(key))//缓存命中
		{
			LFUNode* node = LFUmap[key];
			node->val = value;
			update(node);
		}
		else//缓存没有命中
		{
			if (LFUsize == LFUcapacity)
			{
				Node* node = NULL;
				map<int, DoubleList*>::iterator it1 = freq_map.begin();
				node =it1->second->pop();
				map<int, LFUNode*>::iterator it2 = LFUmap.find(node->val);
				LFUmap.erase(it2);
				--LFUsize;
			}
			LFUNode* lfNode = new LFUNode(value);
			lfNode->freq = 1;
			LFUmap[key] = lfNode;
			if (!freq_map.count(lfNode->freq))
			{
				freq_map[lfNode->freq] = new DoubleList();
			}
			freq_map[lfNode->freq]->append(static_cast<Node*>(lfNode));//
			++LFUsize;
		}
	}

	void printLFU()
	{
		cout << "---------Start-------------------"<< endl;
		for (map<int ,DoubleList*>::iterator it= freq_map.begin();  it != freq_map.end(); ++it)
		{
			cout << "Freq = " << it->first << " :" << endl;
			it->second->printList(); 
		}
		cout << "---------End-------------------"<< endl<<endl;
	}


	int LFUsize;
	int LFUcapacity;
	
	map<int, DoubleList*>freq_map;
	map<int, LFUNode*>LFUmap;

};

测试代码(LFU):

LFUcache cache3(2);
	cache3.put(1, 1);
	cache3.printLFU();

	cache3.put(2, 2);
	cache3.printLFU();

	cout << cache3.get(1) << endl;
	cache3.printLFU();

	cache3.put(3, 3);
	cache3.printLFU();

	cout<<cache3.get(2) << endl;
	cache3.printLFU();
	cout << cache3.get(3) << endl;
	cache3.printLFU();

	cache3.put(4, 4);
	cache3.printLFU();

	cout << cache3.get(1) << endl;
	cache3.printLFU();
	cout << cache3.get(3) << endl;
	cache3.printLFU();
	cout << cache3.get(4) << endl;
	cache3.printLFU();

3. LRU(Least Frequently Used) -- 最近最久未使用

如果一个数据在最近一段时间没有被访问到,那么在将来它被访问的可能性也很小。也就是说,当限定的空间已存满数据时,应当把最久没有被访问到的数据淘汰。(从描述来看和LFU很类似,区别在于,LRU注重的是访问时间,而LFU注重的是访问次数,考察细节不同,但大体方向类似)

实现:核心:双向链表实现队列+ hashmap

用一个双向链表来存储数据,并把相应的地址放入hashmap中,hashmap的作用主要是提高查找效率。当需要插入新的数据项的时候,如果新数据项在链表中存在(一般称为命中),则把该节点移到链表头部;如果不存在,则新建一个节点,放到链表头部。若缓存满了,则把链表最后一个节点删除即可。在访问数据的时候,如果数据项在链表中存在,则把该节点移到链表头部,否则返回-1。

#include"head.h"
#include<math.h>
class LRUcache
{
public:
	LRUcache(int capacity)
	{
		LRUcapacity = capacity;
		list.Listcapcity = capacity;
		LRUsize = 0;
	}
	int get(int key)
	{
		if (LRUmap.count(key))
		{
			Node* node = LRUmap[key];
			list.remove(node);
			list.append_front(node);
			return node->val;
		}
		else
			return - 1;
	}

	void put(int key, int value)
	{
		if (LRUcapacity == 0)
			return;
		if (LRUmap.count(key))
		{
			Node* node = NULL;
			node = list.remove(LRUmap[key]);
			node->val = value;
			list.append_front(node);
		}
		else
		{
			if (LRUsize == LRUcapacity)
			{
				Node* node = NULL;
				node= list.remove();
				int x = node->val;
				map<int, Node*>::iterator it = LRUmap.find(x);
				LRUmap.erase(it);
				--LRUsize;
			}
			Node* node = new Node(value);
			list.append_front(node);
			LRUmap[key] = node;
			++LRUsize;
		}
	}

	void printLRU()
	{
		list.printList();
	}
	//数据成员
	int LRUsize;
	int LRUcapacity;
	DoubleList list;
	map<int, Node*>LRUmap;
};

测试代码(LRU):

    LRUcache cache2(2);
	cache2.put(2, 2);
	cache2.printLRU();

	cache2.put(1, 1);
	cache2.printLRU();

	cache2.put(3, 3);
	cache2.printLRU();

	cout << cache2.get(1) << endl;
	cache2.printLRU();

	cout << cache2.get(2) << endl;
	cache2.printLRU();

	cout << cache2.get(3) << endl;
	cache2.printLRU();

头文件:

#pragma once
#include<iostream>
#include<map>
using namespace std;

class Node
{
public:
	Node() {}
	Node(int x)
	{
		val = x;
	}
	int val = 0;
	Node* prev = NULL;
	Node* next = NULL;
};

class DoubleList
{
public:

	DoubleList(int capacity = 100)
	{
		Listsize = 0;
		head = NULL;
		tail = NULL;
		Listcapcity = capacity;
	}
	//头插入
	bool add_head(Node* node)
	{
		if (head == NULL)
		{
			head = node;
			tail = node;
			head->prev = NULL;
			tail->next = NULL;
			++Listsize;
			return false;
		}
		else
		{
			node->next = head;
			head->prev = node;
			head = node;
			head->prev = NULL;

			++Listsize;
			return true;
		}

	}

	//尾插入
	bool add_tail(Node* node)
	{
		if (tail == NULL)
		{
			head = node;
			tail = node;
			head->prev = NULL;
			tail->next = NULL;
			++Listsize;
			return true;
		}
		else
		{
			node->prev = tail;
			tail->next = node;
			tail = node;
			tail->next = NULL;
			++Listsize;
			return true;
		}
	}

	//任意删除
	Node* removerad(Node* node)
	{
		//node为 NULL 默认删除尾部节点
		if (node == NULL)
			node = tail;
		if (node == tail)
		{
			remove_tail();
			return node;
		}

		else if (node == head)
		{
			remove_head();
			return node;
		}

		else
		{
			node->prev->next = node->next;
			node->next->prev = node->prev;
			--Listsize;
			return node;
		}

	}
	//尾部删除
	Node* remove_tail()
	{
		if (Listsize == 0)
			return NULL;

		else if (Listsize == 1)
		{
			Node* node = tail;
			head = NULL;
			tail = NULL;
			--Listsize;
			return node;
		}
		else
		{
			Node* node = tail;
			tail = tail->prev;
			tail->next = NULL;
			--Listsize;
			return node;
		}
	}
	//头部删除
	Node* remove_head()
	{
		if (Listsize == 0)
			return NULL;

		else if (Listsize == 1)
		{
			Node* node = head;
			head = NULL;
			tail = NULL;
			--Listsize;
			return node;
		}
		else
		{
			Node* node = head;
			head = head->next;
			head->prev = NULL;
			--Listsize;
			return node;
		}
	}

	//外部接口:弹出头部
	Node* pop()
	{
		return remove_head();
	}

	//外部接口: 添加节点  从尾部
	bool append(Node* node)
	{
		return add_tail(node);
	}

	//外部接口: 添加节点  从头部
	bool append_front(Node* node)
	{
		return add_head(node);
	}

	//外部接口: 删除节点
	Node* remove(Node* node = NULL)
	{
		return removerad(node);
	}
	//外部接口: 打印链表
	void printList()
	{
		if (Listsize == 0)
			cout << "No Node..." << endl;
		else {
			Node* cur = head;
			cout << "{ ";
			while (cur != NULL)
			{
				cout << " -> " << cur->val ;
				cur = cur->next;
			}
			cout <<" }"<< endl;
		}

	}
	//数据成员部分
	int Listsize;
	int Listcapcity;
	Node* head;
	Node* tail;
};

 

  • 6
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
以下是基于 C++ 的 OPT, FIFO, LRU 页面置换算法实现代码: ```c++ #include <iostream> #include <vector> #include <queue> #include <algorithm> #include <unordered_map> using namespace std; // OPT 页面置换算法 int OPT(vector<int> pages, int frameSize) { int pageFaults = 0; unordered_map<int, int> nextUse; vector<int> frames(frameSize, -1); for(int i = 0; i < pages.size(); i++) { int page = pages[i]; if(find(frames.begin(), frames.end(), page) != frames.end()) { continue; } if(frames[frames.size() - 1] == -1) { frames[pageFaults] = page; pageFaults++; } else { for(int j = 0; j < frames.size(); j++) { int frame = frames[j]; if(nextUse.find(frame) == nextUse.end()) { nextUse[frame] = i; } else { if(nextUse[frame] < nextUse[frames[j]]) { nextUse[frame] = i; } } } int maxNextUse = -1; int pageToReplace = -1; for(int j = 0; j < frames.size(); j++) { int frame = frames[j]; if(nextUse[frame] > maxNextUse) { maxNextUse = nextUse[frame]; pageToReplace = frame; } } replace(frames.begin(), frames.end(), pageToReplace, page); pageFaults++; } } return pageFaults; } // FIFO 页面置换算法 int FIFO(vector<int> pages, int frameSize) { int pageFaults = 0; queue<int> frames; for(int i = 0; i < pages.size(); i++) { int page = pages[i]; if(find(frames.front(), frames.back(), page) != frames.back()) { if(frames.size() == frameSize) { frames.pop(); } frames.push(page); pageFaults++; } } return pageFaults; } // LRU 页面置换算法 int LRU(vector<int> pages, int frameSize) { int pageFaults = 0; unordered_map<int, int> lastUse; vector<int> frames(frameSize, -1); for(int i = 0; i < pages.size(); i++) { int page = pages[i]; if(find(frames.begin(), frames.end(), page) != frames.end()) { lastUse[page] = i; continue; } if(frames[frames.size() - 1] == -1) { frames[pageFaults] = page; lastUse[page] = i; pageFaults++; } else { int minLastUse = INT_MAX; int pageToReplace = -1; for(int j = 0; j < frames.size(); j++) { int frame = frames[j]; if(lastUse[frame] < minLastUse) { minLastUse = lastUse[frame]; pageToReplace = frame; } } replace(frames.begin(), frames.end(), pageToReplace, page); lastUse[page] = i; pageFaults++; } } return pageFaults; } int main() { vector<int> pages = {1, 2, 3, 4, 1, 2, 5, 1, 2, 3, 4, 5}; int frameSize = 3; cout << "OPT 页面置换算法: " << OPT(pages, frameSize) << endl; cout << "FIFO 页面置换算法: " << FIFO(pages, frameSize) << endl; cout << "LRU 页面置换算法: " << LRU(pages, frameSize) << endl; return 0; } ``` 在这个代码中,我们定义了三个函数 `OPT`, `FIFO`, 和 `LRU`,分别实现了 OPT, FIFO, 和 LRU 页面置换算法。每个函数都接受一个整数数组 `pages` 和一个整数 `frameSize`,其中 `pages` 表示页面序列,`frameSize` 表示内存中页面框的数量。每个函数都返回页面缺页次数。 在 `OPT` 函数中,我们使用了一个哈希表 `nextUse` 来记录每个页面下一次使用的位置。当需要替换页面时,我们选择下一次使用最远的页面进行替换。 在 `FIFO` 函数中,我们使用了一个队列来记录页面。当需要替换页面时,我们选择队列中最早进入的页面进行替换。 在 `LRU` 函数中,我们使用了一个哈希表 `lastUse` 来记录每个页面上一次使用的位置。当需要替换页面时,我们选择上一次使用时间最早的页面进行替换。 注意,以上实现代码仅供参考。实际使用中,还需要考虑到多种情况,比如页面访问的顺序、页面分布的情况等等。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

LionelMessi7

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值