データ構造とアルゴリズムのインタビューの質問: LRU キャッシュを実装し、次の操作をサポートします: 値の取得、値の更新、キーと値のペアの削除、およびキーと値のペアの挿入

データ構造とアルゴリズムのインタビューの質問: LRU キャッシュを実装し、次の操作をサポートします: 値の取得、値の更新、キーと値のペアの削除、およびキーと値のペアの挿入

概要: 次の操作をサポートする LRU キャッシュを実装します: 値の取得、値の更新、キーと値のペアの削除、およびキーと値のペアの挿入

アルゴリズムのアイデア

  1. 二重リンク リストを使用して各キーと値のペアを格納し、アクセス時間に従って早いものから遅いものまで順番に並べます。訪問したノードが遅いほど、二重リンク リストの先頭に近づきます。ここでは C++ のリスト テンプレート クラスが使用されます。

  2. ハッシュ テーブルを使用してキーと対応するノード ポインターを保存します。これは、C++ 標準ライブラリの unowned_map で実装できます。

  3. 挿入、更新、削除の操作では、二重リンク リストとハッシュ テーブルを同時に変更する必要があります。

  4. キャッシュがいっぱいの場合は、新しいキーと値のペアを挿入する前に、最も最近使用されていないノードを二重リンク リストから削除し、対応するキーと値のペアをハッシュ テーブルから削除する必要があります。

  • C++
#include <iostream>
#include <unordered_map>
#include <list>

using namespace std;

class LRUCache {
    
    
public:
    LRUCache(int capacity) {
    
     // 构造函数
        cap = capacity; // 缓存容量
    }
    
    int get(int key) {
    
     // 获取值操作
        if (cache.count(key)) {
    
     // 缓存命中
            auto it = cache[key]; // 从哈希表中找到对应的迭代器
            int val = it->second; // 取出键值对中的值
            recent.erase(it); // 将访问过的键位于链表头部,将其删除
            recent.push_front(key); // 将当前键放在链表头部
            cache[key] = recent.begin(); // 更新键在双向链表中的对应迭代器位置
            return val;
        }
        return -1; // 缓存未命中
    }
    
    void put(int key, int value) {
    
     // 更新值操作
        if (cache.count(key)) {
    
     // 若键已存在,则替换其值
            auto it = cache[key]; // 从哈希表中找到对应的迭代器
            recent.erase(it); // 将访问过的键位于链表头部,将其删除
        }
        else if (recent.size() == cap) {
    
     // 若链表已满,则删除最久未使用的键
            int old_key = recent.back(); // 查找链表尾部的键值并保留
            recent.pop_back(); // 删除链表尾部的键值对
            cache.erase(old_key); // 从哈希表中删除对应的键
        }
        recent.push_front(key); // 将当前键放在链表头部
        cache[key] = recent.begin(); // 更新键在双向链表中的对应迭代器位置
        cache[key]->second = value; // 更新键值对中的值
    }

private:
    int cap;
    unordered_map<int, list<int>::iterator> cache; // 哈希表,键为键值,值为双向链表中对应节点的迭代器
    list<int> recent; // 双向链表,存储键值
};

int main() {
    
    
    LRUCache lru_cache(2);
    lru_cache.put(1, 1);
    lru_cache.put(2, 2);
    cout << lru_cache.get(1) << endl; // 查询键1,返回1,并将该键移动到链表头部
    lru_cache.put(3, 3); // 插入键3,此时容量超出限制,删除最近最少使用的键2
    cout << lru_cache.get(2) << endl; // 查询键2,未命中,返回-1
    lru_cache.put(4, 4); // 插入键4,此时容量超出限制,删除最近最少使用的键1
    cout << lru_cache.get(1) << endl; // 查询键1,未命中,返回-1
    cout << lru_cache.get(3) << endl; // 查询键3,返回3,并将该键移动到链表头部
    cout << lru_cache.get(4) << endl; // 查询键4,返回4,并将该键移动到链表头部
    return 0;
}

  • ジャワ
import java.util.*;

class LRUCache {
    
    
    private int cap;
    private Map<Integer, Integer> cache; // 哈希表,键为键值,值为对应的值
    private Deque<Integer> recent; // 双向链表,存储键值

    public LRUCache(int capacity) {
    
     // 构造函数
        cap = capacity; // 缓存容量
        cache = new HashMap<>();
        recent = new LinkedList<>();
    }

    public int get(int key) {
    
     // 获取值操作
        if (cache.containsKey(key)) {
    
     // 缓存命中
            recent.remove(key); // 移除双向链表中的键
            recent.addFirst(key); // 将当前键放在链表头部
            return cache.get(key); // 返回值
        }
        return -1; // 缓存未命中
    }

    public void put(int key, int value) {
    
     // 更新值操作
        if (cache.containsKey(key)) {
    
     // 若键已存在,则替换其值
            recent.remove(key); // 移除双向链表中的键
        }
        else if (recent.size() == cap) {
    
     // 若链表已满,则删除最久未使用的键
            int old_key = recent.removeLast(); // 查找链表尾部的键值并保留
            cache.remove(old_key); // 从哈希表中删除对应的键值对
        }
        recent.addFirst(key); // 将当前键放在链表头部
        cache.put(key, value); // 更新键值对中的值
    }
}

public class Main {
    
    
    public static void main(String[] args) {
    
    
        LRUCache lru_cache = new LRUCache(2);
        lru_cache.put(1, 1);
        lru_cache.put(2, 2);
        System.out.println(lru_cache.get(1)); // 查询键1,返回1,并将该键移动到链表头部
        lru_cache.put(3, 3); // 插入键3,此时容量超出限制,删除最近最少使用的键2
        System.out.println(lru_cache.get(2)); // 查询键2,未命中,返回-1
        lru_cache.put(4, 4); // 插入键4,此时容量超出限制,删除最近最少使用的键1
        System.out.println(lru_cache.get(1)); // 查询键1,未命中,返回-1
        System.out.println(lru_cache.get(3)); // 查询键3,返回3,并将该键移动到链表头部
        System.out.println(lru_cache.get(4)); // 查询键4,返回4,并将该键移动到链表头部
    }
}

おすすめ

転載: blog.csdn.net/qq_51447496/article/details/131170376
おすすめ