这是我参与8月更文挑战的第5天,活动详情查看:8月更文挑战
前言
- 之前有写过一篇关于LRU淘汰算法的文章 LRU算法记录一下,LRU算法思想是淘汰最近最少使用的元素,当一个元素在一段时间内没有访问过后,那么在之后的一段时间也极有可能不会被访问,然后当数据池满了后将其淘汰掉。
- 这次把关于LFU算法的三种不同实现方式写一下,那废话不多说,show me code
LRU实现之数组 O(N)
/**
* LRU实现之数组 O(N)
*
* @Author: ZRH
* @Date: 2021/8/13 10:32
*/
public class ArrayLru {
/**
* 初始数组
*/
private static Node[] arrNode;
/**
* 数组长度限制大小
*/
private final static Integer SIZE = 3;
private static void add (Object param) {
if (null == param) {
throw new NullPointerException("param must not be null");
}
Node node = new Node(param);
// 添加第一个元素,初始化数组,并把第一个元素放入数组
if (arrNode == null) {
arrNode = new Node[SIZE];
arrNode[0] = node;
return;
}
// 最大num数的数组下标
int index = 0;
// 标识是否已经前置赋值
boolean fal = true;
for (int i = 0; i < SIZE; i++) {
// 当前节点为null,并且添加的新节点还未赋值到数组,则赋值
if (null == arrNode[i]) {
if (fal) {
arrNode[i] = node;
fal = false;
}
break;
}
// 添加的新节点为当前节点,则直接改num
if (fal && arrNode[i].value.equals(node.value)) {
arrNode[i].num = 0;
fal = false;
break;
}
// 找到最大num的数组下标
if (i == 0) {
index = i;
} else if (arrNode[i - 1].num < arrNode[i].num) {
index = i;
}
// 为数组其他所有节点num+1
arrNode[i].addNum();
}
// 标识新加入的节点已经赋值,直接跳出
if (!fal) {
return;
}
// 把最大num元素替换为新加入的元素(num数越大,说明元素就没有重新赋值为0 ,说明就最久没有使用)
arrNode[index] = node;
}
@Data
static class Node<T> {
private final T value;
private Integer num;
public Node (T t) {
this.value = t;
this.num = 0;
}
public void addNum () {
this.num = this.num + 1;
}
}
public static void main (String[] args) {
add("a");
System.out.println(JSON.toJSONString(arrNode));
add("b");
System.out.println(JSON.toJSONString(arrNode));
add("c");
System.out.println(JSON.toJSONString(arrNode));
add("c");
System.out.println(JSON.toJSONString(arrNode));
add("d");
System.out.println(JSON.toJSONString(arrNode));
add("e");
System.out.println(JSON.toJSONString(arrNode));
}
}
---------------
打印结果:
[{"num":0,"value":"a"},null,null]
[{"num":1,"value":"a"},{"num":0,"value":"b"},null]
[{"num":2,"value":"a"},{"num":1,"value":"b"},{"num":0,"value":"c"}]
[{"num":3,"value":"a"},{"num":2,"value":"b"},{"num":0,"value":"c"}]
[{"num":0,"value":"d"},{"num":3,"value":"b"},{"num":1,"value":"c"}]
[{"num":1,"value":"d"},{"num":0,"value":"e"},{"num":2,"value":"c"}]
- 这里数组实现算法还有优化的点:当最后插入新数据时数组已满了,然后发现数组有两个数据的访问标识相等且都值都是最大,那么还应该在这两个数据中找到在数组里待的时间较长的数据,然后把这个数据进行淘汰。
LRU实现之链表 O(N)
/**
* LRU实现之链表 O(N)
*
* @Author: ZRH
* @Date: 2021/8/13 11:11
*/
public class LinkedLru {
/**
* 初始链表
*/
private static LinkedList<Object> list;
/**
* 链表长度限制大小
*/
private final static Integer SIZE = 3;
private static void add (Object param) {
if (null == param) {
throw new NullPointerException("param must not be null");
}
// 添加第一个元素时,初始化链表,并放入元素
if (null == list) {
list = new LinkedList<>();
list.addFirst(param);
return;
}
// 遍历链表,若添加的元素在链表中已存在,则把元素移动到首位
for (Object o : list) {
if (o.equals(param)) {
list.remove(param);
list.addFirst(param);
return;
}
}
// 若链表元素大小达到限制,则删除链表尾元素
if (list.size() == SIZE) {
list.removeLast();
}
// 添加元素到头节点
list.addFirst(param);
}
public static void main (String[] args) {
add("a");
System.out.println(JSON.toJSONString(list));
add("b");
System.out.println(JSON.toJSONString(list));
add("c");
System.out.println(JSON.toJSONString(list));
add("a");
System.out.println(JSON.toJSONString(list));
add("b");
System.out.println(JSON.toJSONString(list));
add("b");
System.out.println(JSON.toJSONString(list));
add("b");
System.out.println(JSON.toJSONString(list));
add("d");
System.out.println(JSON.toJSONString(list));
}
}
--------------
打印结果:
["a"]
["b","a"]
["c","b","a"]
["a","c","b"]
["b","a","c"]
["b","a","c"]
["b","a","c"]
["d","b","a"]
LRU实现之hash O(1)
/**
* LRU实现之hash O(1)
*
* @Author: ZRH
* @Date: 2021/8/13 11:32
*/
public class HashLru {
/**
* 将添加的元素放入hash中,用于快速查找定位
* 这里key自定义生成,value是链表节点
*/
private static Map<String, Node> map;
/**
* 存放元素的链表,用于快速删除和插入
* 这里放入的节点可以包含:元素和map中的key。这样设计是可以通过链表能定位map ,通过map也能快速定位链表
*/
private static LinkedList<Node> list;
/**
* 链表长度限制大小
*/
private final static Integer SIZE = 3;
private static void add (Object param) {
if (null == param) {
throw new NullPointerException("param must not be null");
}
// 添加第一个元素时,初始化hash表和链表,并放入元素
String key = String.valueOf(param);
Node firstNode = new Node(key, param);
if (null == map) {
map = new HashMap<>(8);
list = new LinkedList<>();
map.put(key, firstNode);
list.addFirst(firstNode);
return;
}
// 通过hash查找当前添加的元素是否已经存在hash中,如果存在,则移动元素到链表头节点
Node node = map.get(key);
if (null != node) {
list.remove(node);
list.addFirst(node);
return;
}
// 链表中元素大小达到限制长度,则删除链表中尾节点元素
if (list.size() == SIZE) {
Node lastNode = list.getLast();
map.remove(lastNode.key);
list.remove(lastNode);
}
// 头节点插入新元素
map.put(key, firstNode);
list.addFirst(firstNode);
}
@Data
static class Node {
@JSONField(serialize = false)
private String key;
private Object value;
public Node (String key, Object value) {
this.key = key;
this.value = value;
}
}
public static void main (String[] args) {
add("a");
System.out.println(JSON.toJSONString(list));
add("b");
System.out.println(JSON.toJSONString(list));
add("c");
System.out.println(JSON.toJSONString(list));
add("a");
System.out.println(JSON.toJSONString(list));
add("b");
System.out.println(JSON.toJSONString(list));
add("b");
System.out.println(JSON.toJSONString(list));
add("b");
System.out.println(JSON.toJSONString(list));
add("d");
System.out.println(JSON.toJSONString(list));
}
}
--------------
打印结果:
[{"value":"a"}]
[{"value":"b"},{"value":"a"}]
[{"value":"c"},{"value":"b"},{"value":"a"}]
[{"value":"a"},{"value":"c"},{"value":"b"}]
[{"value":"b"},{"value":"a"},{"value":"c"}]
[{"value":"b"},{"value":"a"},{"value":"c"}]
[{"value":"b"},{"value":"a"},{"value":"c"}]
[{"value":"d"},{"value":"b"},{"value":"a"}]
最后
- 上诉的代码可以直接copy,就可以运行得到结果,不同实现方式的思想和细节也在代码备注里面
- 如果上诉代码有什么错误的地方欢迎提出,我也会加以改正
- 虚心学习,共同进步 -_-