力扣146题、460题、牛客100(LRU缓存机制、LFU算法)
146.LRU缓存机制
基本思想:
需要做到两点:
1.
快读找到某个key是否有对应的val。(用字典)
2.
要支持在任意的位置快速插入和删除元素。(用链表)
结合起来就是哈希链表
使用双向链表和哈希表结合
为什么使用双向链表?
具体实现:
规定越靠近头部是最新的元素,越靠近尾部是越旧的元素
代码:
import java.util.*; public class Solution { private int capacity; private Map<Integer, Node> map; private Node head; private Node tail; private int used; class Node { int key; int value; Node prev; Node next; Node(int key, int value, Node prev, Node next) { this.key = key; this.value = value; this.prev = prev; this.next = next; } } public Solution(int capacity) { // write code here this.capacity = capacity; this.map = new HashMap<>(); this.used = 0; } public int get(int key) { // write code here if (!map.containsKey(key)) { return -1; } makeRecently(key); return map.get(key).value; } public void set(int key, int value) { // 如果 key 已存在,直接修改值,再移到链表头部 if (map.containsKey(key)) { map.get(key).value = value; makeRecently(key); return; } // 如果达到容量上限,就要移除尾部节点,注意 HashMap 要 remove!!! if (used == capacity) { map.remove(tail.key); tail = tail.prev; tail.next = null; used--; } // 头节点为空,单独处理 if (head == null) { head = new Node(key, value, null, null); tail = head; } else { Node t = new Node(key, value, null, head); head.prev = t; head = t; } map.put(key, head); used++; } // 把 key 对应的节点移到链表头部 private void makeRecently(int key) { Node t = map.get(key); if (t != head) { if (t == tail) { tail = tail.prev; tail.next = null; } else { t.prev.next = t.next; t.next.prev = t.prev; } t.prev = null; t.next = head; head.prev = t; head = t; } } } /** * Your Solution object will be instantiated and called as such: * Solution solution = new Solution(capacity); * int output = solution.get(key); * solution.set(key,value); */
460、LFU算法
基本思想:
具体实现:
定义两个字典:
1.第一个:key-to-node,
key对应的value
2.第二个:双向链表
freq对应key与value
目的:利用两个哈希表来使得两个操作的时间复杂度均为O(1)
get(key)操作:
判断key是否存在
存在的话,在原有的频率对应的列表[key:value;key:value;key:value]中,删除这个key:value
判断是否改变了最小频率
将这个key对应的freq+1
再将这key:value放入新的freq对应的列表中
put(key,value)操作:
插入的key如果原来就存在,做法如同get
插入的key不存在的话
判断容量是否满了,满了就删除最小频率对应的key
不满的话就是一个新的key,出现频率为1
代码:
import java.util.*; public class Solution { //设置节点结构 static class Node{ int freq; int key; int val; //初始化 public Node(int freq, int key, int val) { this.freq = freq; this.key = key; this.val = val; } } //频率到双向链表的哈希表 private Map<Integer, LinkedList<Node> > freq_mp = new HashMap<>(); //key到节点的哈希表 private Map<Integer, Node> mp = new HashMap<>(); //记录缓存剩余容量 private int size = 0; //记录当前最小频次 private int min_freq = 0; public int[] LFU (int[][] operators, int k) { //构建初始化连接 //链表剩余大小 this.size = k; //获取操作数 int len = (int)Arrays.stream(operators).filter(x -> x[0] == 2).count(); int[] res = new int[len]; //遍历所有操作 for(int i = 0, j = 0; i < operators.length; i++){ if(operators[i][0] == 1) //set操作 set(operators[i][1], operators[i][2]); else //get操作 res[j++] = get(operators[i][1]); } return res; } //调用函数时更新频率或者val值 private void update(Node node, int key, int value) { //找到频率 int freq = node.freq; //原频率中删除该节点 freq_mp.get(freq).remove(node); //哈希表中该频率已无节点,直接删除 if(freq_mp.get(freq).isEmpty()){ freq_mp.remove(freq); //若当前频率为最小,最小频率加1 if(min_freq == freq) min_freq++; } if(!freq_mp.containsKey(freq + 1)) freq_mp.put(freq + 1, new LinkedList<Node>()); //插入频率加一的双向链表表头,链表中对应:freq key value freq_mp.get(freq + 1).addFirst(new Node(freq + 1, key, value)); mp.put(key, freq_mp.get(freq + 1).getFirst()); } //set操作函数 private void set(int key, int value) { //在哈希表中找到key值 if(mp.containsKey(key)) //若是哈希表中有,则更新值与频率 update(mp.get(key), key, value); else{ //哈希表中没有,即链表中没有 if(size == 0){ //满容量取频率最低且最早的删掉 int oldkey = freq_mp.get(min_freq).getLast().key; //频率哈希表的链表中删除 freq_mp.get(min_freq).removeLast(); if(freq_mp.get(min_freq).isEmpty()) freq_mp.remove(min_freq); //链表哈希表中删除 mp.remove(oldkey); } //若有空闲则直接加入,容量减1 else size--; //最小频率置为1 min_freq = 1; //在频率为1的双向链表表头插入该键 if(!freq_mp.containsKey(1)) freq_mp.put(1, new LinkedList<Node>()); freq_mp.get(1).addFirst(new Node(1, key, value)); //哈希表key值指向链表中该位置 mp.put(key, freq_mp.get(1).getFirst()); } } //get操作函数 private int get(int key) { int res = -1; //查找哈希表 if(mp.containsKey(key)){ Node node = mp.get(key); //根据哈希表直接获取值 res = node.val; //更新频率 update(node, key, res); } return res; } }