欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

每日LeeteCode-实现LRU cache

程序员文章站 2022-03-26 20:35:47
题目描述实现一个K-V的数据结构, key和value都是int类型,value为正整数1.有固定的大小2.实现get(), set()方法,要求时间复杂度为O(1)3.当容量满的时候,采用LRU淘汰策略面试的时候拿到这道题还是挺懵的无从下手,没有在规定时间内做出来.现在回过头来想,其实要求给的蛮详细的,只要冷静下来分析下,还是挺简单的题目分析定义一个类来实现这个数据结构: LRUCache有固定大小?既然给了固定大小,就需要有属性来存储这个值,并且是不可变的private fina....

题目描述
实现一个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.淘汰最早放进来的数据
为了实现这个功能,我使用双向链表的数据结构实现:
每日LeeteCode-实现LRU cache1.定义空节点为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-实现LRU cache
如果想更好地验证自己的代码是否成功,可以在leeteCode上进行调试
调试链接: https://leetcode-cn.com/problems/lru-cache/

本文地址:https://blog.csdn.net/weixin_43188031/article/details/107590420

相关标签: 每日LeetCode