LeetCode 146 LRU缓存机制

题目描述

运用你所掌握的数据结构,设计和实现一个  LRU (最近最少使用) 缓存机制。它应该支持以下操作: 获取数据 get 和 写入数据 put 。

获取数据 get(key) - 如果密钥 (key) 存在于缓存中,则获取密钥的值(总是正数),否则返回 -1。
写入数据 put(key, value) - 如果密钥不存在,则写入其数据值。当缓存容量达到上限时,它应该在写入新数据之前删除最近最少使用的数据值,从而为新的数据值留出空间。

进阶:

你是否可以在 O(1) 时间复杂度内完成这两种操作?

示例:

LRUCache cache = new LRUCache( 2 /* 缓存容量 */ );

cache.put(1, 1);
cache.put(2, 2);
cache.get(1);       // 返回  1
cache.put(3, 3);    // 该操作会使得密钥 2 作废
cache.get(2);       // 返回 -1 (未找到)
cache.put(4, 4);    // 该操作会使得密钥 1 作废
cache.get(1);       // 返回 -1 (未找到)
cache.get(3);       // 返回  3
cache.get(4);       // 返回  4

分析:

1、使用栈和hashmap来做:(效率不高)

get:

如果hashmap中有的话,就去栈中,把相对应的节点放到栈顶去,这样栈顶就是最近最常使用的了;如果hashmap中没有的话,就返回-1。

 put:

如果hashmap中已有这个key值,就更新value的值,并且在栈中找到元素放到栈顶;

如果hashmap中没有这个key值,先把hashmap的元素个数(size)和设置的缓存capacity做比较:

       (1)size<capacity,容量够,可以直接往里放:把元素放进hashmap中,并且把元素放栈顶;

       (2)否则,容量不够,把栈底元素(最近没有使用到的)删除,并把这个元素在hashmap中也删除。然后把新元素放进hashmap中,并且把新元素放到栈顶。

2、使用双向链表和hashmap:(效率高)

栈的底层存储是维护一个数组,增删操作的时候,会动荡整个数组,使用链表只需要改变指针方向,效率更高。

与方法1 差不多,只是栈顶改成了链表的头指针(经常使用),栈底改成了链表的尾指针(不常使用)。

get:

如果hashmap中有的话,就去链表中,把相对应的节点放到链表头部去,这样链表头部就是最近最常使用的了;如果hashmap中没有找到的话,就返回-1。

 put:

如果hashmap中已有这个key值,就更新value的值,并且在链表中找到元素放到链表头部;

如果hashmap中没有这个key值,先把hashmap的元素个数(size)和设置的缓存capacity做比较:

       (1)size<capacity,容量够,可以直接往里放:把元素放进hashmap中,并且把元素放链表头部;

       (2)否则,容量不够,把链尾元素(最近没有使用到的)删除,并把这个元素在hashmap中也删除。然后把新元素放进hashmap中,并且把新元素放到链表头部。

Java实现:

注:双向链表头尾节点都使用空节点。真正数据节点是除去这两个节点。

import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;

/**
 * @ClassName:LURCache
 * @description:
 * @Author: why
 * @Date: 2019/4/20
 */
public class LRUCache {
    private class Node {
        private int key;
        private int value;

        private Node pre = null;
        private Node next = null;

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

        Node() {
        }

    }

    private Map<Integer, Node> map;

    //缓存容量
    private int capacity;

    private Node tail = new Node();

    private Node head = new Node();

    //元素个数
    private int size;

    LinkedList

    LRUCache(int capacity) {
        this.capacity = capacity;
        map = new HashMap<>(capacity);
        size = 0;
        head.next = tail;
        tail.pre = head;
    }

    public void deleteNode(Node node) {
        Node preNode = node.pre;
        Node nextNode = node.next;

        preNode.next = nextNode;
        nextNode.pre = preNode;

        node.pre = null;
        node.next = null;
    }

    public void addNode(Node node) {
        Node headNext = head.next;

        head.next = node;
        headNext.pre = node;

        node.pre = head;
        node.next = headNext;
    }

    public void put(int key, int value) {

        Node node = map.get(key);

        if (node != null) {

            node.value = value;

            deleteNode(node);

            addNode(node);

        } else {

            node = new Node(key, value);

            if (size < capacity) {
                size++;
            } else {
                //删掉链尾(最近最少用的),map也要删!!
                map.remove(tail.pre.key);
                deleteNode(tail.pre);
            }

            //加到链头,并加到map!!!
            addNode(node);
            map.put(key, node);
        }
    }

    public int get(int key) {
        Node node = map.get(key);

        if (node == null) {

            return -1;
        }
        deleteNode(node);

        addNode(node);

        return node.value;

    }
    
}

测试:

18 / 18 个通过测试用例

状态:通过

执行用时:142 ms

提交时间:0 分钟之前

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值