【算法系列】LRU和LFU的算法实现

介绍

Least Recently Used (LRU),最近最少使用。如果一个对象最近没有被使用,那么一段时间内都不会被使用。

Least Frequently Used (LFU),如果一个对象使用频率越低,那么再次被使用的该类也越低。

LRU

LRU缓存

代码实现

重写LinkedHashMap

class LRUCache {

    private int capacity;

    private LinkedHashMap<Integer, Integer> store;


    public LRUCache(int capacity) {
        this.capacity = capacity;
        store = new LinkedHashMap<Integer, Integer>(capacity, 0.75F, true) {
            @Override
            protected boolean removeEldestEntry(Map.Entry<Integer, Integer> eldest) {
                return size() > capacity;
            }
        };
    }

    public int get(int key) {
        return store.getOrDefault(key, -1);
    }

    public void put(int key, int value) {
        store.put(key, value);
    }
}

使用HashMap和双链表

public class LRUCache2 {

    private Map<Integer, DLinkedNode> cache;

    private DLinkedList dLinkedList;

    private int size;

    private int capacity;

    public LRUCache2(int capacity) {
        this.capacity = capacity;
        cache = new HashMap<>();
        dLinkedList = new DLinkedList();
    }

    public int get(int key) {
        DLinkedNode node = cache.get(key);
        if (node == null) {
            return -1;
        } else {
            dLinkedList.moveToHead(node);
        }
        return node.getVal();
    }

    public void put(int key, int value) {
        DLinkedNode node = cache.get(key);
        if (node == null) {
            DLinkedNode newNode = new DLinkedNode(key, value);
            dLinkedList.addToHead(newNode);
            cache.put(key, newNode);
            size++;
            if (size > capacity) {
                DLinkedNode dLinkedNode = dLinkedList.removeTail();
                cache.remove(dLinkedNode.getKey());
                size--;
            }
        } else {
            node.setVal(value);
            dLinkedList.moveToHead(node);
        }
    }


    static class DLinkedList {

        private DLinkedNode head;

        private DLinkedNode tail;

        public DLinkedList() {
            head = new DLinkedNode();
            tail = new DLinkedNode();
            head.next = tail;
            tail.prev = head;
        }

        public DLinkedNode removeTail() {
            DLinkedNode node = tail.prev;
            tail.prev.prev.next = tail;
            tail.prev = tail.prev.prev;
            return node;
        }

        public void moveToHead(DLinkedNode node) {
            removeNode(node);
            addToHead(node);
        }

        public void removeNode(DLinkedNode node) {
            DLinkedNode prev = node.prev;
            DLinkedNode next = node.next;

            prev.next = next;
            next.prev = prev;
        }

        public void addToHead(DLinkedNode node) {
            //head永远是head节点
            head.next.prev = node;
            node.next = head.next;
            head.next = node;
            node.prev = head;
        }


    }


    public static void main(String[] args) {
        LRUCache2 lruCache2 = new LRUCache2(2);
        lruCache2.put(1, 1);
        lruCache2.put(2, 2);
        lruCache2.get(1);
        lruCache2.put(3, 3);
        int res = lruCache2.get(2);
        System.out.println(res);
    }

}

在这里插入图片描述

实现思路

  1. 含义:最近最少使用的。
  2. 第一种实现方式。linkedHashMap自带,重写removeEldestEntry,判断size>capacity(capacity是常量),构造方法第三个参数是accessOrder,设置为true。
  3. 第二种实现方式。hashmap和双链表的形式。hashmap用来获取元素,双链表是为了清除元素。
  4. 双链表的优点,在于获取节点,就可以知道前后节点的信息。删除尾结点需要获取对应的节点信息,以便从缓存中删除。所有的put操作,除了操作key之外,还要更新val的信息。双链表可以知道哪个节点是先插入的,哪个节点是后插入的。
  5. 双链表需要提供的操作。增加到头结点,删除中间节点。
  6. hash表,value存放的是node,是为了找到双链表的前后节点信息。

LFU

LFU 缓存

代码实现

public class LFUCache {

    private Map<Integer, Node> cache = new HashMap<>();
    private Map<Integer, LinkedList<Node>> freqMap = new HashMap<>();

    private int capacity;

    private int minFreq;

    public LFUCache(int capacity) {
        this.capacity = capacity;
    }

    public int get(int key) {
        Node node = cache.get(key);
        if (node == null) {
            return -1;
        } else {
            incr(node);
        }
        return node.val;

    }

    public void put(int key, int value) {
        Node node = cache.get(key);
        if (node == null) {
            if (this.capacity == 0) {
                return;
            }
            if (cache.size() == capacity) {
                removeMinFreNode();
            }
            Node newNode = new Node(key, value, 1);
            this.incr(newNode, true);
            cache.put(key, newNode);
        } else {
            node.val = value;
            incr(node);
        }

    }


    public void incr(Node node) {
        incr(node, false);
    }

    private void incr(Node node, boolean isNewNode) {
        if (isNewNode) {
            minFreq = 1;
            insertToLinkedList(node);
        } else {
            deleteNode(node);
            node.freq++;
            insertToLinkedList(node);
            if (!freqMap.containsKey(minFreq) || freqMap.get(minFreq).size() == 0) {
                minFreq++;
            }
        }
    }

    public void insertToLinkedList(Node node) {
        if (freqMap.get(node.freq) == null) {
            this.freqMap.put(node.freq, new LinkedList());
        }
        LinkedList<Node> nodes = this.freqMap.get(node.freq);
        nodes.addFirst(node);
    }

    public void deleteNode(Node node) {
        LinkedList<Node> linkedList = this.freqMap.get(node.freq);
        linkedList.remove(node);
    }

    private void removeMinFreNode() {
        LinkedList<Node> nodes = freqMap.get(minFreq);
        Node node = nodes.removeLast();
        cache.remove(node.key);
    }

    protected class Node {
        public int key;
        public int val;

        public int freq;
        public Node prev;
        public Node next;

        public Node(int key, int value, int freq) {
            this.key = key;
            this.val = value;
            this.freq = freq;
        }
    }


    public static void main(String[] args) {
        LFUCache lfu = new LFUCache(0);
        lfu.put(0, 0);
        lfu.get(0);

    }
}

实现思路

  1. 含义:最近最少访问频率
  2. 实现逻辑,有两个map,一个存储key和节点信息;另一个存储,频次和有该频次的节点链表。
  3. 存在capacity为空的情况,节点为null,不允许存进去。
  4. 获取key。如果存在,移除节点,更新频率,存入节点;如果不存在,返回-1。
  5. 插入key和val。如果存在节点,需要更新val,移除节点,更新频率,存入节点。如果不存在节点,等同于新增节点,新增节点要判断是不是capacity已经满了,满了需要删除最小频率节点的链表的数据。存入cache。
  6. 一旦是旧节点更新频率,就要更新最小频率值,如果是新节点插入,最小频率设置为1。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值