首页 > 试题广场 >

设计LRU缓存结构

[编程题]设计LRU缓存结构
  • 热度指数:1171 时间限制:C/C++ 2秒,其他语言4秒 空间限制:C/C++ 256M,其他语言512M
  • 算法知识视频讲解
设计LRU缓存结构,该结构在构造时确定大小,假设大小为K,并有如下两个功能
  • set(key, value):将记录(key, value)插入该结构
  • get(key):返回key对应的value值
[要求]
  1. set和get方法的时间复杂度为O(1)
  2. 某个key的set或get操作一旦发生,认为这个key的记录成了最常使用的。
  3. 当缓存的大小超过K时,移除最不经常使用的记录,即set或get最久远的。

输入描述:
第一行两个个整数N, K,表示操作数量以及缓存结构大小
接下来N行,第一行一个整数opt表示操作类型。
若opt=1,接下来两个整数x, y,表示set(x, y)
若opt=2,接下来一个整数x,表示get(x),若x未出现过或已被移除,则返回-1


输出描述:
对于每个操作2,输出一个答案
示例1

输入

6 3
1 1 1
1 2 2
1 3 2
2 1
1 4 4
2 2

输出

1
-1

说明

第一次操作后:最常使用的记录为("1", 1)
第二次操作后:最常使用的记录为("2", 2),("1", 1)变为最不常用的
第三次操作后:最常使用的记录为("3", 2),("1", 1)还是最不常用的
第四次操作后:最常用的记录为("1", 1),("2", 2)变为最不常用的
第五次操作后:大小超过了3,所以移除此时最不常使用的记录("2", 2),加入记录("4", 4),并且为最常使用的记录,然后("3", 2)变为最不常使用的记录

备注:

#include <bits/stdc++.h>
using namespace std;

struct P{
    int k, v;
};

class LRU{
public:
    int K;
    list<P> cache;
    unordered_map<int, list<P>::iterator> M;
    LRU(int k){
        K = k;
    }
    int get(int k){
        if(M.find(k) == M.end())
            return -1;
        P p = *M[k];
        cache.erase(M[k]);
        cache.push_front(p);
        return p.v;
    }
    void put(int k, int v){
        if(M.find(k) == M.end()){
            if(cache.size() == K){
                P p = cache.back();
                M.erase(p.k);
                cache.pop_back();
            }
            cache.push_front(P{k,v});
            M[k] = cache.begin();
        }else{
            cache.erase(M[k]);
            cache.push_front(P{k,v});
            M[k] = cache.begin();
        }
    }
};

int main(){
    int n, k, opt, x, y;
    scanf("%d%d", &n, &k);
    LRU lru(k);
    for(int i=0;i<n;i++){
        scanf("%d", &opt);
        if(opt==1){
            scanf("%d%d", &x, &y);
            lru.put(x, y);
        }else if(opt==2){
            scanf("%d", &x);
            printf("%d\n", lru.get(x));
        }
    }
    return 0;
}

发表于 2020-06-13 01:16:14 回复(0)
用LinkedHashMap,保持key的顺序,以便在容量达到k还需往map中添加元素时获得第一个key并删除。需要注意的是,如果map中需要添加重复的key,则需要先把老的key删掉,然后重新添加键值对,以保证这个刚操作的key又成为了最新的key
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;
import java.util.LinkedHashMap;

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String[] params = br.readLine().split(" ");
        int n = Integer.parseInt(params[0]), k = Integer.parseInt(params[1]);
        LinkedHashMap<Integer, Integer> map = new LinkedHashMap<>();
        StringBuilder sb = new StringBuilder();
        for(int i = 0; i < n; i++){
            params = br.readLine().split(" ");
            int opt = Integer.parseInt(params[0]), x = Integer.parseInt(params[1]);
            if(opt == 1){
                int y = Integer.parseInt(params[2]);
                if(map.size() < k){
                    if(map.containsKey(x)) map.remove(x);     // 重新设置过,也要将设置的key在链表中调到末尾
                }else{
                    // 把最老的key删除
                    int firstKey = map.keySet().iterator().next();
                    map.remove(firstKey);
                }
                map.put(x, y);
            }else{
                if(!map.containsKey(x)){
                    sb.append(-1 + "\n");
                }else{
                    sb.append(map.get(x) + "\n");
                    // 将这个键值对改为最新使用,追加到链表的最后
                    int y = map.get(x);
                    map.remove(x);
                    map.put(x, y);
                }
            }
        }
        System.out.println(sb.toString().trim());
    }
}

发表于 2021-11-21 21:13:46 回复(0)

思路:将LRU设置为一个双向链表的结构。

如果有新的元素加入,则往双向链表的尾节点中插入;
如果删除最近最少的节点,则删除双向链表的头节点。
为了方便操作双向链表,我们设置了2个头尾的哑节点dummyHead和dummyTail。

import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;

public class Main {

    private int capacity; // LRU最大容量
    private Map<Integer, Node> map = new HashMap<>(); // 映射表用于获取元素
    private Node dummyHead = new Node(0, 0); // 哑节点(头)
    private Node dummyTail = new Node(0, 0); // 哑节点(尾)

    /**
     * LRU的节点是一个双向链表中的一个节点
     */
    class Node {
        int key;
        int value;
        Node prev;
        Node next;

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

    /**
     * 初始化最大容量
     * @param capacity LRU最大容量
     */
    public Main(int capacity) {
        this.capacity = capacity;
        dummyHead.next = dummyTail;
        dummyTail.prev = dummyHead;
    }

    /**
     * 往LRU中添加元素
     * @param key
     * @param value
     */
    public void set(int key, int value) {
        // 如果映射表已包含该key,则要从中删除
        if (map.containsKey(key)) {
            remove(map.get(key));
        }
        // 如果当前元素数量已经达到最大值,要删除LRU的头节点
        if (map.size() == capacity) {
            remove(dummyHead.next);
        }
        Node node = new Node(key, value);
        add(node);
    }

    /**
     * 从LRU中查数据
     * @param key
     * @return
     */
    public int get(int key) {
        // 如果映射表中包含该key,则要先从LRU中删除该节点,再将该节点插入到LRU尾部
        if (map.containsKey(key)) {
            Node node = map.get(key);
            remove(node);
            add(node);
            return node.value;
        }
        return -1;
    }

    /**
     * 删除一个节点
     * @param node
     */
    public void remove(Node node) {
        map.remove(node.key);
        node.next.prev = node.prev;
        node.prev.next = node.next;
    }

    /**
     * 添加一个节点
     * @param node
     */
    public void add(Node node) {
        map.put(node.key, node);
        node.next = dummyTail;
        node.prev = dummyTail.prev;
        dummyTail.prev = node;
        node.prev.next = node;
    }

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int k = sc.nextInt();
        Main lruCache = new Main(k);
        while (n-- > 0) {
            int operation = sc.nextInt();
            if (operation == 1) {
                int key = sc.nextInt();
                int value = sc.nextInt();
                lruCache.set(key, value);
            } else if (operation == 2) {
                int value = sc.nextInt();
                System.out.println(lruCache.get(value));
            }
        }
    }
}
发表于 2019-10-28 11:39:53 回复(0)

问题信息

上传者:小小
难度:
3条回答 4205浏览

热门推荐

通过挑战的用户

查看代码