LRU algorithm
LRU is the abbreviation of Least Recently Used, that is, the Least Recently Used algorithm. It is a page replacement algorithm in the operating system. Because the number of pages stored in memory is limited, the recently unused algorithm will be moved out of memory.
LRU principle:
The principle of LRU is shown in the figure below:
It will put the most recently accessed nodes in front and the nodes that have not been accessed recently in the back. If the cache space is full, the last node will be deleted and a new node will be added to the header.
Java implementation
For the linked storage structure, the collections used include ArrayList and LinkedList. The bottom layer of ArrayList is based on array, and the time complexity of moving elements is high. Therefore, LinkedList is used, which is based on the form of linked list and is a two-way linked list, because nodes should be deleted. The search time complexity of linked list is also high, but it can be located quickly by using hash. Therefore, LRU algorithm can be implemented through bidirectional linked list and hash.
Code part
Manual implementation of two-way linked list:
import java.util.HashMap; import java.util.Map; public class LRUCache { //Define map mapping private Map<String, Node> map; //Define cache capacity private int capacity = 8; //There is currently a data in the cache private int size = 0; //Head node and tail node of linked list private Node head, tail; //Read data from cache public int get(String key) { Node node = map.get(key); //If it can be found, move the node to the head of the linked list without returning - 1 if (node != null) { moveToHead(node); return node.value; } return -1; } //Add node to cache public void put(String key, int value) { if(capacity == 0) return; Node node = map.get(key); if (node != null) { //If it exists, modify the value of this point and move it to the head of the linked list node.value = value; moveToHead(node); return; } //If it does not exist, first judge whether the cache space is sufficient if (size == capacity) { map.remove(tail.pre.key); deleteNode(tail.pre); size -= 1; } Node newNode = new Node(key, value); addToHead(newNode); map.put(key, newNode); size += 1; } //Move node to head node private void moveToHead(Node node) { deleteNode(node); addToHead(node); } //Add node to header private void addToHead(Node node) { node.next = head.next; head.next.pre = node; node.pre = head; head.next = node; } //Delete this node private void deleteNode(Node node) { node.pre.next = node.next; node.next.pre = node.pre; } public LRUCache(int capacity) { this.capacity = capacity; initLinkedList(); map = new HashMap<>(capacity); size = 0; } //Initialize linked list private void initLinkedList() { head = new Node(); tail = new Node(); head.next = tail; tail.pre = head; } public LRUCache() { initLinkedList(); map = new HashMap<>(this.capacity); } public static class Node { public Node pre; public Node next; public String key; public int value; public Node(String key, int value) { this.key = key; this.value = value; } public Node() { } } public static void main(String[] args) { LRUCache cache = new LRUCache(2); cache.put("key1", 1); cache.put("key2", 2); System.out.println(cache.get("key2"));//21 cache.put("key3", 3); System.out.println(cache.get("key1"));//32 } }
Call the LinkedList API for implementation
import java.util.HashMap; import java.util.LinkedList; import java.util.Map; public class LRUCache { //Define map mapping private Map<String, Node> map; //Define cache capacity private int capacity = 8; private LinkedList<Node> list = new LinkedList<>(); //Read data from cache public int get(String key) { Node node = map.get(key); //If it can be found, move the node to the head of the linked list without returning - 1 if (node != null) { list.remove(node); list.addFirst(node); return node.value; } return -1; } //Add node to cache public void put(String key, int value) { if(capacity == 0) return; Node node = map.get(key); if (node != null) { //If it exists, modify the value of this point and move it to the head of the linked list node.value = value; list.remove(node); list.addFirst(node); return; } //If it does not exist, first judge whether the cache space is sufficient if (list.size() == capacity) { //If it exists, delete the in the map and LinkedList map.remove(list.getLast().key); list.removeLast(); } Node newNode = new Node(key, value); map.put(key, newNode); list.addFirst(newNode); } public LRUCache(int capacity) { this.capacity = capacity; //It also defines the capacity of the map. In fact, this step is not necessary map = new HashMap<>(capacity); } public LRUCache() { map = new HashMap<>(this.capacity); } public static class Node { public String key; public int value; public Node(String key, int value) { this.key = key; this.value = value; } public Node() { } } public static void main(String[] args) { LRUCache cache = new LRUCache(2); cache.put("key1",1); cache.put("key2",2); System.out.println(cache.get("key2")); //21 cache.put("key3",3); System.out.println(cache.get("key1")); //32 } }
LFU algorithm
LFU is the abbreviation of Least Frequently Used, that is, the least recent access frequency. It is also a page exchange algorithm in the operating system. It calls out the pages that have been used least recently and have not been used for a long time.
LFU principle
Among them, one map stores the relationship between frequency and the collection of nodes, that is, freq - list, and the other map stores the relationship between key - node. Read the node from the key - node, and then go to freq - list to add the node to the corresponding list set
Code part
Since a linked list must be created for each frequency to store, it is implemented directly using the API in Java
package cache; import java.util.HashMap; import java.util.LinkedList; import java.util.Map; public class LFUCache { private Map<String,Node> key_node; private Map<Integer, LinkedList<Node>> freq_list; private int capacity = 2; private int minFreq = 1; public int get(String key){ //If this number is not included, return - 1 if(!key_node.containsKey(key)) return -1; Node node = key_node.get(key); //Get the frequency of the node int freq = node.freq; //Current node frequency plus 1 node.freq += 1; //Deletes a node from the corresponding collection freq_list.get(freq).remove(node); //If the deleted node is 0, delete the collection if(freq_list.get(freq).size() == 0){ freq_list.remove(freq); if(freq == minFreq){ minFreq += 1; } } LinkedList<Node> list = freq_list.getOrDefault(freq + 1, new LinkedList<Node>()); list.addFirst(node); freq_list.put(freq + 1, list); return node.value; } public void put(String key,int value){ if(capacity == 0) return ; Node node = key_node.get(key); if(node != null){ //Gets the frequency of the current node int freq = node.freq; node.freq += 1; node.value = value; //Deletes a node from the set of corresponding frequency nodes freq_list.get(freq).remove(node); //If there are no elements in the collection, the collection is deleted if(freq_list.get(freq).size() == 0){ freq_list.remove(freq); //If the minimum frequency is deleted, add 1 to the minimum frequency if(freq == minFreq){ minFreq += 1; } } //Move the node to the node set of the next frequency LinkedList<Node> list = freq_list.getOrDefault(freq + 1, new LinkedList<Node>()); list.offerFirst(node); freq_list.put(freq + 1, list); key_node.put(key, node); }else { Node newNode = new Node(key,value,1); //If the cache capacity is full, you need to delete the data that has been used the least frequently and has not been used for a long time if(key_node.size() == capacity){ Node lastNode = freq_list.get(minFreq).pollLast(); key_node.remove(lastNode.key); if(freq_list.get(minFreq).size() == 0){ freq_list.remove(minFreq); } } //Add a new node to the collection key_node.put(key,newNode); LinkedList<Node> list = freq_list.getOrDefault(1, new LinkedList<Node>()); list.offerFirst(newNode); freq_list.put(1,list); minFreq = 1; } } public LFUCache(int capacity) { this.capacity = capacity; key_node = new HashMap<>(); freq_list = new HashMap<>(); } public LFUCache() { key_node = new HashMap<>(); freq_list = new HashMap<>(); } public static class Node{ public String key; public int value; public int freq; public Node() { } public Node(String key, int value, int freq) { this.key = key; this.value = value; this.freq = freq; } } public static void main(String[] args) { LFUCache cache = new LFUCache(2); cache.put("key1",1); //1 cache.put("key2",2); //21 cache.put("key3",3); //32 System.out.println(cache.get("key1")); System.out.println(cache.get("key2")); System.out.println(cache.get("key3")); cache.put("key1",1); //13 System.out.println(cache.get("key1")); System.out.println(cache.get("key2")); System.out.println(cache.get("key3")); cache.put("key2",2); //21 cache.put("key1",1); //12 } }