手撕LRU的三种写法

解题思路

  • LRU总体上是这样的,最近使用的放在最左边,最近没用的放到最右边,来了一个新的数,如果内存满了,把旧的数淘汰掉
  • 解决:链表(处理新老关系)+哈希(查询在不在),分析如下
  1. 底层应该用链表,按照数据的新旧程度来排列,旧的在左边,新的在右边,新来一个加到尾部,删除是删头,除了这两个操作,还有就是把一个数据从中间拿出来放尾巴上(这个数组就很难做到)
  2. 这里还有一个需求,就是要知道这个数据有没有存在于链表中,如果不在链表中,加到尾巴即可,如果已经在链表中,就只要更细数据的位置,如何查找这个数据在不在呢,这就用哈希表。
  3. 考虑删除操作,要把当前节点的前一个节点的指针的改变,获取它前一个节点,方便的数据结构就是 双向链表

所以我们用的数据结构就是 LinkedList(底层是双向链表)+ HashMap,而实际上可以直接用LinkedHashMap更为方便。看面试官要求是啥了。

ps:其实也可以用单链表。。我在map中不存当前节点,而是存当前节点的前驱即可。

下面把三种方式都写一下

代码

解法一:使用LinkedHashMap

public class LRUCache{
    int capacity;
    Map<Integer, Integer> map;

    public LRUCache(int capacity) {
        this.capacity = capacity;
        map = new LinkedHashMap<>();
    }

    public int get(int key) {
        if (!map.containsKey(key)) {
            return -1;
        }
        //先删除就的位置,再放入新位置
        Integer value = map.remove(key);
        map.put(key, value);
        return value;
    }


    public void put(int key, int value) {
        if (map.containsKey(key)) {
            map.remove(key);
            map.put(key, value);
            return;
        }
        map.put(key, value);
        //超出capacity,删除最久没用的,利用迭代器,删除第一个
        if (map.size() > capacity) {
            map.remove(map.entrySet().iterator().next().getKey());
        }
    }
}

解法二:使用双链表+HashMap

public class LRUCache{

    //定义双向链表节点
    private class ListNode {
        int key;
        int val;
        ListNode pre;
        ListNode next;

        public ListNode(int key, int val) {
            this.key = key;
            this.val = val;
            pre = null;
            next = null;
        }
    }

    private int capacity;
    private Map<Integer, ListNode> map;     //key->node
    private ListNode head;  //dummy head
    private ListNode tail;  //dummy tail

    public LRUCache(int capacity) {
        this.capacity = capacity;
        map = new HashMap<>();
        head = new ListNode(-1, -1);
        tail = new ListNode(-1, -1);
        head.next = tail;
        tail.pre = head;
    }

    public int get(int key) {
        if (!map.containsKey(key)) {
            return -1;
        }
        ListNode node = map.get(key);
        //先把这个节点删除,再接到尾部
        node.pre.next = node.next;
        node.next.pre = node.pre;
        moveToTail(node);

        return node.val;
    }

    public void put(int key, int value) {
        //直接调用这边的get方法,如果存在,它会在get内部被移动到尾巴,不用再移动一遍,直接修改值即可
        if (get(key) != -1) {
            map.get(key).val = value;
            return;
        }
        //不存在,new一个出来,如果超出容量,把头去掉
        ListNode node = new ListNode(key, value);
        map.put(key, node);
        moveToTail(node);

        if (map.size() > capacity) {
            map.remove(head.next.key);
            head.next = head.next.next;
            head.next.pre = head;
        }
    }

    private void moveToTail(ListNode node) {
        node.pre = tail.pre;
        tail.pre = node;
        node.pre.next = node;
        node.next = tail;
    }
}

解法三:使用单链表

public class LRUCache{

    private class ListNode {
        int key, val;
        ListNode next;

        public ListNode(int key, int val) {
            this.key = key;
            this.val = val;
            this.next = null;
        }
    }

    private int capacity;
    private Map<Integer, ListNode> map;     //key-> node.pre
    private ListNode head;  //dummy
    private ListNode tail;

    public LRUCache(int capacity) {
        this.capacity = capacity;
        map = new HashMap<>();
        head = new ListNode(-1, -1);
        tail = head;
    }

    public int get(int key) {
        if (!map.containsKey(key)) {
            return -1;
        }
        //map中存放的是要找的节点的前驱
        ListNode pre = map.get(key);
        ListNode cur = pre.next;

        //把当前节点删掉并移到尾部
        if (cur != tail) {
            pre.next = cur.next;
            map.put(cur.next.key, pre); //更新它后面node的前驱
            map.put(cur.key, tail);
            moveToTail(cur);
        }
        return cur.val;
    }

    public void put(int key, int value) {
        if (get(key) != -1) {
            map.get(key).next.val = value;
            return;
        }
        //不存在就new一个
        ListNode node = new ListNode(key, value);
        map.put(key, tail); //当前node的pre是tail
        moveToTail(node);

        if (map.size() > capacity) {
            map.remove(head.next.key);
            map.put(head.next.next.key, head);
            head.next = head.next.next;
        }
    }

    private void moveToTail(ListNode node) {
        node.next = null;
        tail.next = node;
        tail = tail.next;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值