Niuke.netブラッシングの質問-LRUキャッシュ構造の設計

問題の説明

タイトル説明
LRUキャッシュ構造を設計します。構造のサイズは構築中に決定されます。サイズをKとすると、次の2つの関数が
設定されます(key、value):レコード(key、value)を構造に挿入します
get( key):return key対応する値value
[要件]
setメソッドとgetメソッドの時間計算量はO(1)
です。特定のキーのsetまたはget操作が発生すると、このキーのレコードが最も一般的に使用されると見なされます。 。
キャッシュのサイズがKを超える場合は、使用頻度の最も低いレコード、つまり最も古いセットまたはgetを削除します。
opt = 1の場合、次の2つの整数x、yはset(x、y)を表し
ます。opt= 2の場合、次の整数xはget(x)を表します。xが表示されていないか削除されている場合は、-1
返します。各操作2、回答を出力します

例1

入力
[[1,1,1]、[1,2,2]、[1,3,2]、[2,1]、[1,4,4]、[2,2]、3

出力
[1、-1]

説明
最初の操作の:最も頻繁に使用されるレコードは( "1"、1)
2番目の操作の:最も頻繁に使用されるレコードは( "2"、2)、( "1"、1)が最も使用頻度の低いレコードになります
後に第3の動作:最も頻繁に使用されるレコードである(「3」、2)、(「1」、1)又は少なくとも頻繁に使用
した後に第4の動作:最も頻繁に使用されるレコードである(「1」、1)、( "2"、2)は、最も使用頻度の低い
5番目の操作になります。サイズが3を超えるため、この時点で最も使用頻度の低いレコード( "2"、2)を削除し、レコード( "4"、4)を追加します。が最も頻繁に使用されるレコードである場合、( "3"、2)は最も使用頻度の低いレコードになります

ソリューション

分析

  1. JavaのHashMapの助けを借りて、ストレージの問題を考慮する必要はありません。リンクリスト(最も最近使用されていない)を介してlruを実装するだけで済みます。
  2. 元のハッシュストレージを実現するためにJavaのHashMapを使用しないでください(後で追加)

コード

// 思路1
public class Solution {
    
      
        /**
     * lru design
     * @param operators int整型二维数组 the ops
     * @param k int整型 the k
     * @return int整型一维数组
     */
    public int[] LRU (int[][] operators, int k) throws Exception {
    
    

        LRU<Integer, Integer> lru = new LRU<>(k);
        List<Integer> list = new ArrayList<>();
        // write code here
        for (int i = 0; i < operators.length; i++) {
    
    
            int[] operator = operators[i];
            if (operator[0] == 1) {
    
    
                lru.put(operator[1], operator[2]);
            } else if (operator[0] == 2) {
    
    
                Integer temp = lru.get(operator[1]);
                if (temp == null) {
    
    
                    list.add(-1);
                } else {
    
    
                    list.add(temp);
                }
            }
        }

        int[] result = new int[list.size()];
        for (int i = 0; i < list.size(); i++) {
    
    
            result[i] = list.get(i);
        }
        return result;
    }
}

// 重新考虑,LRU:最近最少使用
// 数据结构:链表(可以通过现有的,也可以通过自定义的node节点)、map集合
// 方法:LRU:put方法,get方法;
// addFirst,添加到头部节点
// remove,移除节点
class LRU<K, V> {
    
    

    // 头尾节点作为空节点
    private Node head = new Node();
    private Node tail = new Node();
    // 记录K-Node映射,便于快速查找目标数据对应节点
    private HashMap<K, Node> map;
    private int maxSize;

    // 哈希链表节点类 Node
    private class Node {
    
    
        Node pre;
        Node next;
        K k;
        V v;

        public Node() {
    
    
        }

        // Node对外提供构造方法
        public Node(K k, V v) {
    
    
            this.k = k;
            this.v = v;
        }
    }

    // 初始化时必须传入最大可用内存容量
    public LRU(int maxSize) {
    
    
        this.maxSize = maxSize;
        // HashMap初始容量设置为 maxSize * 4/3,即达到最大可用内存时,HashMap也不会自动扩容浪费空间
        // 通过反向计算,可以避免map的扩容
        this.map = new HashMap<>(maxSize * 4 / 3);

        // 设置头节点的后置节点和尾节点的前置节点
        head.next = tail;
        tail.pre = head;
    }

    // 获取指定数据
    public V get(K key) {
    
    
        // 判断是否存在对应数据
        if (!map.containsKey(key)) {
    
    
            return null;
        }

        // 最新访问的数据移动到链表头,从链表中移除节点并追加到尾部
        Node node = map.get(key);
        remove(node);
        addFirst(node);
        return node.v;
    }

    // 更新旧数据或添加数据
    public void put(K key, V value) {
    
    
        System.out.println("K:" + key + ";V="+value);
        // 若存在旧数据则删除
        if (map.containsKey(key)) {
    
    
            Node node = map.get(key);
            remove(node);
        }

        // 新数据对应节点插入链表头
        Node node = new Node(key, value);
        map.put(key, node);
        addFirst(node);

        // 判断是否需要淘汰数据
        if (map.size() > maxSize) {
    
    
            node = removeLast();
            // 数据节点淘汰后,同时删除map中的映射
            map.remove(node.k);
        }
    }

    // 将指定节点插入链表头
    private void addFirst(Node node) {
    
    
        Node next = head.next;

        // 设置追加的节点为当前节点的后置节点
        head.next = node;
        // 设置当前节点的前置节点为头节点
        node.pre = head;

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

    // 从链表中删除指定节点
    private void remove(Node node) {
    
    
        Node pre = node.pre;
        Node next = node.next;

        // 跳过当前节点
        pre.next = next;
        // 设置下一个节点的前置节点
        next.pre = pre;

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

    // 淘汰数据
    private Node removeLast() {
    
    
        // 找到最近最久未使用的数据所对应节点
        Node node = tail.pre;

        // 淘汰该节点
        remove(node);

        return node;
    }
}

テストしたい場合は、Niuke.comのリンクに直接アクセスしてテストを行うことができます

LRUキャッシュ構造を設計する

おすすめ

転載: blog.csdn.net/qq_35398517/article/details/113960142