内容简介:我有一个朋友面试的时候被问到过,要手撸一个LRU算法,所以我也准备一下好了。在进程运行过程中,若其所要访问的页面不在内存而需把它们调入内存,但内存这里通常把选择换出页面的算法称为
前言
我有一个朋友面试的时候被问到过,要手撸一个LRU算法,所以我也准备一下好了。
页面置换算法
在进程运行过程中,若其所要访问的页面不在内存而需把它们调入内存,但内存 「 已无空闲空间 」 时,为了保证该进程能正常运行,系统必须从内存中调出一页程序或数据送磁盘的对换区中。但应将哪个页面调出,须根据一定的算法来确定。
这里通常把选择换出页面的算法称为 「 页面置换算法 」 (Page-Replacement Algorithms)。置换算法的好坏, 将直接影响到系统的性能。
一个好的页面置换算法,应具有 「 较低的页面更换频率 」 。从理论上讲,应将那些以后不再会访问的页面换出,或把那些在较长时间内不会再访问的页面调出。存在着许多种置换算法,它们都试图更接近于理论上的目标。
LRU
LRU是Least Recently Used的缩写,即 「 最近最少使用 」 ,是一种常用的页面置换算法,选择最近最久未使用的页面予以淘汰。
该算法赋予每个页面一个访问字段,用来记录一个页面自上次被访问以来所经历的时间 t,当须淘汰一个页面时,选择现有页面中其 t 值最大的,即最近最少使用的页面予以淘汰。
简单来说 如果一个数据在 「 最近一段时间 」 内 「 使用次数很少 」 ,那么在将来一段时间内被使用的可能性也很小 ,所以我如果 「 内存到了一定限制 」 ,可以先把它淘汰掉这样。
伪代码「其实这就够了」
这里的话我选择用 双向链表 + HashMap 的写法:
-
首先定义一下Node节点
public class Node {
private int key;
private int val;
private Node pre;
private Node next;
public Node(int key, int val) {
this.key = key;
this.val = val;
this.pre = null;
this.next = null;
}
}
-
定义双向链表
// 这里的链表只存头和尾,主要是控制最近最少使用「head」,和最新使用的「tail」
public class LinkedList {
private Node head;
private Node tail;
// 最新使用的数据的话就需要把数据移动到尾
public void moveToTail(Node n) {
// 将已存在的n移到尾部
}
// 最新使用的新数据就加入到尾
public void addToTail(Node n) {
// 将n加入到tail后面
}
// 到达限制要移除头部
public Node removeHead() {
// 移除头部然后返回
}
}
-
LRUCache
public class LRUCache {
private HashMap<Integer, Node> map;
private LinkedList list;
private capacity;
public int get(int key) {
// 获取Node
// 刚用完,移到尾部
// 返回
}
public void put(int key, int val) {
// 查看一下map原来有没有
// 有 -> 修改val并移到尾部(刚用完)
// 无 -> 1. 加入尾部,放进map
// 无 -> 2. 比较map的大小跟限制的长度capacity,大了就移除最近最少使用的head,小了不管就ok。
}
}
完整代码及分析
import java.util.HashMap;
/**
* LRUCache
* @author tonghua
* @create 2020-03-14 16:53
*/
public class LRUCache {
// 双向链表控制头尾
private LinkedList list;
// hashmap保存数据
private HashMap<Integer, Node> map;
// capacity限制LRUCache的大小
private int capacity;
public LRUCache(int capacity) {
this.map = new HashMap<>();
this.list = new LinkedList();
this.capacity = capacity;
}
public class Node {
public int key;
public int val;
Node pre;
Node next;
public Node(int key, int val) {
this.key = key;
this.val = val;
this.pre = null;
this.next = null;
}
}
// head为最近最少使用的
public class LinkedList {
public Node head;
public Node tail;
public LinkedList() {
this.head = null;
this.tail = null;
}
// 最新使用的数据的话就需要把数据移动到尾
public void moveToTail(Node n) {
// 如果用的是头,那么把头往后移一个
if (n == head) {
head = n.next;
head.next = null;
} else {
n.pre.next = n.next;
n.next.pre = n.pre;
}
// 把对应的n移动到尾部
tail.next = n;
n.pre = tail;
n.next = null;
tail = n;
}
// 最新使用的新数据就加入到尾
public void addToTail(Node n) {
// 如果是第一个,头跟尾都是n
if (head == null) {
head = n;
tail = n;
} else {
// 不是第一个就往尾后加n,尾后移一个
tail.next = n;
n.pre = tail;
tail = tail.next;
}
}
// 移除最近最少使用元素
public Node removeHead() {
Node n = head;
// 这种情况只会出现在capacity=1的情况
if (head == tail) {
head = null;
tail = null;
} else {
// 正常应该是把头移除,然后返回头(给hashMap移除用的)
head = head.next;
head.pre = null;
}
return n;
}
}
public int get(int key) {
if (!map.containsKey(key)) {
return -1;
} else {
Node n = map.get(key);
list.moveToTail(n);
return n.val;
}
}
public void put(int key, int val) {
if (map.containsKey(key)) {
Node n = map.get(key);
n.val = val;
list.moveToTail(n);
} else {
Node n = new Node(key, val);
map.put(key, n);
list.addToTail(n);
if (map.size() > capacity) {
map.remove(list.removeHead().key);
}
}
}
}
PS
都结合网上资料加上自己的一些理解,如果有影响到人的地方,可以联系我: fzfz2007@163.com
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
The Web Designer's Idea Book, Vol. 2
Patrick McNeil / How / 2010-9-19 / USD 30.00
Web Design Inspiration at a Glance Volume 2 of The Web Designer's Idea Book includes more than 650 new websites arranged thematically, so you can easily find inspiration for your work. Auth......一起来看看 《The Web Designer's Idea Book, Vol. 2》 这本书的介绍吧!