LRU实现(手动双向链表+HashMap;LinkedHashMap)

方法一:手动
整体需要两个模块,HashMap用于存储key及其对应值,链表用于存储各个节点使用时间排序(因为要频繁删除、添加,用双向链表方便操作,时间复杂度降到O(1))

首先构造双向链表节点类:

class Node {
        int val;
        int key;
        Node pre;
        Node next;

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

其次依据该节点类构造双向链表类:

class DoubleLinked {
        private Node head;
        private Node tail;
        private int size;

        public DoubleLinked() {
            head = new Node(0, 0);
            tail = new Node(0, 0);
            head.next = tail;
            tail.pre = head;
            this.size = 0;
        }
}

头尾两个节点并不存储数据,因为在该环境下,从头到位表示使用由旧到新,随时需要头结点用于去除第一个(最老)节点,尾结点用于插入新节点。

思考所要用到的方法,首先队尾是最近使用的节点,那么有方法用于直接在队尾插入节点(更新最新节点的关键)。伴随着最新节点就是在队尾添加该节点,那么原节点也应该被删除,这就是第二个方法,移除指定节点
当容量放不下,需要淘汰最老节点,这就是第三个方法,移除第一个节点

		public void addLast(Node node) {
            node.next = tail;
            node.pre = tail.pre;
            tail.pre.next = node;
            tail.pre = node;
            size++;
        }

        public void removeNode(Node node) {
            node.pre.next = node.next;
            node.next.pre = node.pre;
            size--;
        }

        public Node removeFirst() {
            if (head == tail) {
                return null;
            }
            Node first = head.next;
            removeNode(first);
            return first;
        }

有了双向链表工具,就可以开始写真正LRU实现部分:
再实现其get和put方法前,有一些前置需要考虑清楚。
get流程:
首先在map查找是否有所需节点,没有直接返回-1,若有需要将该节点提升为最近使用,返回该节点值。
put流程:
首先map查找是否有节点,没有需要将该节点记录为最近使用,并在map中添加映射关系,再判断是否超容量,超了需要除去最老节点;有则需要删除原节点,将有新值的节点更新为最近使用。

上述流程有公共部分可以集成为对应方法,如:
删除节点:由Map获取节点后,将其在链表、map都删除
提升节点最近使用:在删除节点后,在链表尾部添加
添加新节点:在map中添加映射关系,在链表尾部添加节点
删除最老节点:删除链表头节点,map删除映射

private void makePriority(int key) {
        Node node = map.get(key);
        doubleLinked.removeNode(node);
        doubleLinked.addLast(node);
    }

    private void addRecent(int key, int val) {
        Node res = new Node(key, val);
        doubleLinked.addLast(res);
        map.put(key, res);
    }

    private void deleteKey(int key) {
        Node node = map.get(key);
        doubleLinked.removeNode(node);
        map.remove(key);
    }

    private void removeOld() {
        Node node = doubleLinked.removeFirst();
        map.remove(node.key);
    }

搭配方法即可实现最终get、put方法:

public int get(int key) {
        if (!map.containsKey(key)) {
            return -1;
        }
        makePriority(key);
        return map.get(key).val;
    }

    public void put(int key, int val) {
        if (map.containsKey(key)) {
            deleteKey(key);
            addRecent(key, val);
            return;
        }

        if (doubleLinked.size > this.cap) {
            removeOld();
        }
        addRecent(key, val);
    }

整体:

public class LRU {
    class Node {
        int val;
        int key;
        Node pre;
        Node next;

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

    class DoubleLinked {
        private Node head;
        private Node tail;
        private int size;

        public DoubleLinked() {
            head = new Node(0, 0);
            tail = new Node(0, 0);
            head.next = tail;
            tail.pre = head;
            this.size = 0;
        }

        public void addLast(Node node) {
            node.next = tail;
            node.pre = tail.pre;
            tail.pre.next = node;
            tail.pre = node;
            size++;
        }

        public void removeNode(Node node) {
            node.pre.next = node.next;
            node.next.pre = node.pre;
            size--;
        }

        public Node removeFirst() {
            if (head == tail) {
                return null;
            }
            Node first = head.next;
            removeNode(first);
            return first;
        }

        public int getSize() {
            return size;
        }
    }

    private HashMap<Integer, Node> map;
    private DoubleLinked doubleLinked;
    private int cap;

    public LRU(int cap) {
        this.map = new HashMap<>();
        this.doubleLinked = new DoubleLinked();
        this.cap = cap;
    }

    private void makePriority(int key) {
        Node node = map.get(key);
        doubleLinked.removeNode(node);
        doubleLinked.addLast(node);
    }

    private void addRecent(int key, int val) {
        Node res = new Node(key, val);
        doubleLinked.addLast(res);
        map.put(key, res);
    }

    private void deleteKey(int key) {
        Node node = map.get(key);
        doubleLinked.removeNode(node);
        map.remove(key);
    }

    private void removeOld() {
        Node node = doubleLinked.removeFirst();
        map.remove(node.key);
    }

    public int get(int key) {
        if (!map.containsKey(key)) {
            return -1;
        }
        makePriority(key);
        return map.get(key).val;
    }

    public void put(int key, int val) {
        if (map.containsKey(key)) {
            deleteKey(key);
            addRecent(key, val);
            return;
        }

        if (doubleLinked.size > this.cap) {
            removeOld();
        }
        addRecent(key, val);
    }
}

方法二:
java本身容器LinkedHashMap就是在HashMap(无序)基础上添加了保持加入顺序的特性,完美符合需求。
注:LinkedHashMap只有在新加入键值对才会排序,更改已有键对应值并不会排序。

public class LRU {
    int cap;
    LinkedHashMap<Integer, Integer> cache = new LinkedHashMap<>();

    public LRU(int cap) {
        this.cap = cap;
    }

    private void makePriority(int key) {
        int val = cache.get(key);
        cache.remove(key);
        cache.put(key, val);
    }

    public int get(int key) {
        if (!cache.containsKey(key)) {
            return -1;
        }
        makePriority(key);
        return cache.get(key);
    }

    public void put(int key, int val) {
        if (cache.containsKey(key)) {
            cache.put(key, val);
            makePriority(key);
            return;
        }
        if (cache.size() > this.cap) {
            int old = cache.keySet().iterator().next();
            cache.remove(old);
        }
        cache.put(key, val);
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

魔幻音

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值