每日LeeteCode-实现LRU cache

题目描述
实现一个K-V的数据结构, key和value都是int类型,value为正整数
1.有固定的大小
2.实现get(), set()方法,要求时间复杂度为O(1)
3.当容量满的时候,采用LRU淘汰策略

面试的时候拿到这道题还是挺懵的无从下手,没有在规定时间内做出来.现在回过头来想,其实要求给的蛮详细的,只要冷静下来分析下,还是挺简单的

题目分析

定义一个类来实现这个数据结构: LRUCache

有固定大小?
既然给了固定大小,就需要有属性来存储这个值,并且是不可变的

private final int capacity;

面试官给的是300的固定值,在这我通过构造函数的方式将数值填入

LRUCache(int capacity) {
	this.capacity = capacity;
}

此外, 我们还需要定义一个属性来存储当前的存储个数

private final int count;

实现get(), set()方法,要求时间复杂度为O(1)?
要求是K-V结构, 有要求get,set都是O(1), 这种典型的数据结构,自然而然想到了HashMap, 那么我们暂且使用HashMap来存储数据吧

private Map<Integer, Integer> map = HashMap<>();

当容量满的时候,采用LRU淘汰策略
这个就有些难度了, 首先我们来了解下什么事LRU淘汰策略:
1.淘汰最少使用的数据
2.淘汰最早放进来的数据
为了实现这个功能,我使用双向链表的数据结构实现:
在这里插入图片描述1.定义空节点为head和tail -》卡左右边界,减少为空判断
2.新写入的节点插入到head后面
3.如果某个数据被get了,就将这个节点从原位置移动到head后面
4.如果容量满了, 删除尾节点,将前一个节点设置成尾节点,清空新的尾节点数据,达到删除数据的效果
申明Node类来存储节点数据:

class Node {
    public int key;	// key
    public int value;	// value
    public Node prev;	// 前置节点
    public Node next;	// 后置节点
}

在LRUCache中申明双向链表的head和tail节点,并在构造函数中进行初始化操作:

private Node head;
private Node tail;

public LRUCache(int capacity) {
   this.capacity = capacity;	// 第一步的容量设定
   head = new Node();
   tail = new Node();
   head.next = tail;	// 头节点和尾节点链接起来,node节点get的时候插入进来就行
   tail.prev = head;
 }

HashMap和双向链表如何绑定?
通过以上分析后, 我们在LRUCache类里面申明了两种数据结构: HashMap和双向链表,一个负责get.set的O(1)效率,一个负责LRU淘汰策略的实现,那我们又该如何将两者结合起来呢?
这里, 我们修改下HashMap的申明, key不变,将它的value改为Node,这样,我们就通过Node作为中间介质将两者联系到一起啦

private Map<Integer, Node> map = new HashMap<>();

代码实现

对上述分析进行整合,完整地实现LRUCache

public class LRUCache {
    
    private static class Node {

        public int key;
        public int value;
        public Node prev;
        public Node next;

        public Node() {
            this.key = -1;
            this.value = -1;
            this.prev = null;
            this.next = null;
        }

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

    private final int capacity;

    private Map<Integer, Node> map = new HashMap<>();

    private Node head;

    private Node tail;

    private int count = 0;

    public LRUCache(int capacity) {
        this.capacity = capacity;
        head = new Node();
        tail = new Node();
        head.next = tail;
        tail.prev = head;
    }

    public int get(int key) {
        Node node = map.get(key);
        if (node == null) {
            return -1;
        }
        moveToHead(node);
        return node.value;
    }

    public void put(int key, int value) {
        // key存在,对旧值覆盖
        if (map.containsKey(key)) {
            Node node = map.get(key);
            node.value = value;
            moveToHead(node);
            return;
        }
        // 内存已满 删除最后一个节点再添加
        if (count == capacity) {
            removeLast();
            addNewNode(key, value);
            return;
        }
        // 添加新节点,当前内存数+1
        addNewNode(key, value);
        count++;
    }

    /**
     * 添加新节点
     */
    private void addNewNode(int key, int value) {
        Node node = new Node(key, value);
        node.prev = head;
        node.next = head.next;
        head.next.prev = node;
        head.next = node;
        map.put(key, node);
    }

    /**
     * 将节点移动到head
     */
    private void moveToHead(Node node) {
        if (head.next != node) {
            node.prev.next = node.next;
            node.next.prev = node.prev;
            node.prev = head;
            node.next = head.next;
            head.next.prev = node;
            head.next = node;
        }
    }

    /**
     * 删除最后一个节点
     */
    private void removeLast() {
        tail = tail.prev;
        tail.value = -1;
        tail.next = null;
        map.remove(tail.key);
    }
}

最后, 来看下实际效果吧!

public class Test {

    public static void main(String[] args) {

        LRUCache lruCache = new LRUCache(2);

        lruCache.put(1, 1);
        lruCache.put(2, 2);
        System.out.println(lruCache.get(1));
        lruCache.put(3, 3);
        System.out.println(lruCache.get(2));
        lruCache.put(4, 4);
        System.out.println(lruCache.get(1));
        System.out.println(lruCache.get(3));
        System.out.println(lruCache.get(4));
    }
}

在这里插入图片描述
如果想更好地验证自己的代码是否成功,可以在leeteCode上进行调试
调试链接: https://leetcode-cn.com/problems/lru-cache/

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值