146. LRU 緩存機制

題目描述

題目思路

  1. 因爲需要o(1)級別的時間完成緩存操作,決定了我們對數據結構的設計需要快速的查找,刪除(刪除長時間未使用的),因爲單列表具有不錯的插入,刪除性能,但是任然不能達到O(1)級別,因此採用雙向列表進行記錄緩存元素的使用情況,雙向列表在插入和刪除上能做到O(1)級別的操作方式。

  2. 因爲需要優秀的讀取性能,因此考慮使用HashMap進行存取。

思路設計
  1. 因爲設計到雙向列表,因此數據存儲的數據結構設計爲Node節點。
  2. 對於雙向列表需要提供功能爲:
    • 需要提供節點刪除功能。
    • 需要提供節點切換功能,即需要把讀取的節點提升到最活躍的情況。
    • 需要提供刪除最久未使用的節點功能。
  3. 對於緩存實現幫助類,我們需要實現如下方法:
    • 獲取對象的時候,需要把已經存在的對象活躍度刷新到最活躍的狀態。
    • 放入對象的時候,需要把對象同步到hashmap和狀態列表中去。

代碼實現

import java.util.HashMap;

/**
 * @description:
 * @author: lilang
 * @version:
 * @modified By:[email protected]
 */


public class LRUCache {


    private class Node {

        private Node next;
        private Node pre;
        private int key;
        private int val;

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

    private class TwoWayList {

        private Node first;

        private Node last;

        private int size;

        /**
         * 初始化雙向列表,新加入的節點會放到雙向列表頭尾節點之間
         *
         * 活躍度判斷:新加入的節點都統一放到末尾,因此越靠近末尾的節點越活越
         */
        public TwoWayList() {
            first = new Node(0, 0);
            last = new Node(0, 0);
            first.next = last;
            last.pre = first;
            this.size = 0;
        }

        /**
         * 節點添加到末尾,新加入的節點和上一個節點和尾部節點建立雙向連接
         * 同時列表長度加一
         * @param node
         */
        public void addNodeToLast(Node node) {

            node.pre = last.pre;
            node.next = last;
            last.pre.next = node;
            last.pre = node;
            size++;
        }

        /**
         * 刪除某個節點,進入來需要刪除的節點,必須存在
         *
         * @param node
         */
        public void remove(Node node) {

            node.pre.next = node.next;

            node.next.pre = node.pre;

            size--;

        }

        /**
         * 返回刪除後的節點
         *
         * @return
         */
        public Node removeFirst() {
            /**
             * 默認雙向列表保持兩個頭尾節點
             */
            if (first.next.next == null) {
                return null;
            }
            Node removeNode = first.next;
            remove(first.next);
            return removeNode;
        }


        public int getSize() {
            return size;
        }
    }

    private int capacity;

    /**
     * hashmap 存儲node有一個好處就是可以獲得node的地址信息
     *
     */

    private HashMap<Integer, Node> cacheMap;

    private TwoWayList twoWayList;


    public LRUCache(int capacity) {
        this.capacity = capacity;
        this.cacheMap = new HashMap();
        this.twoWayList = new TwoWayList();

    }

    public int get(int key) {
        Node node = cacheMap.get(key);

        if (node == null) {

            return -1;
        }

        makeNodeRecently(node);

        return node.val;

    }

    public void put(int key, int value) {
        Node node = cacheMap.get(key);

        if (node == null){
            if (twoWayList.getSize() == this.capacity) {
                removeLeastRecently();
            }
            addRecently(key, value);

        }else {
            node.val = value;
            cacheMap.put(key,node);
            /**
             * 提升當前節點的活躍度
             */
            makeNodeRecently(node);
        }


    }


    /* 將某個Node提升爲最近使用的 */
    private void makeNodeRecently(Node node) {
        twoWayList.remove(node);
        twoWayList.addNodeToLast(node);

    }

    /* 添加最近使用的元素 */
    private void addRecently(int key, int val) {
        Node node = new Node(key, val);
        twoWayList.addNodeToLast(node);
        cacheMap.put(key, node);

    }

    /* 刪除最久未使用的元素 */
    private void removeLeastRecently() {
        Node node = twoWayList.removeFirst();
        if (node != null) {
            cacheMap.remove(node.key);
        }

    }


    public static void main(String[] args) {
        LRUCache lruCache = new LRUCache(2);

        lruCache.put(1,1);
        lruCache.put(2,2);
        lruCache.get(1);
        lruCache.put(3,3);
        lruCache.get(2);
        lruCache.put(4,4);
        lruCache.get(1);
        lruCache.get(3);
        lruCache.get(4);

    }
}

Java原始工具類來寫

class LRUCache {



    private int capacity;

    private LinkedHashMap<Integer, Integer> linkedHashMap;

    public LRUCache(int capacity) {
        this.capacity = capacity;
        linkedHashMap = new LinkedHashMap<>();
    }

    public int get(int key) {
        if (linkedHashMap.containsKey(key)) {
            makeKeyRecent(key, linkedHashMap.get(key));
        }
        return linkedHashMap.getOrDefault(key, -1);
    }

    public void put(int key, int value) {

        if (linkedHashMap.containsKey(key)) {
            makeKeyRecent(key, value);
            return;

        }

        if (linkedHashMap.size() == capacity) {
            deleteOldestKey();
        }
         makeKeyRecent(key, value);
    }

    public void makeKeyRecent(int key, int val) {

        linkedHashMap.remove(key);
        linkedHashMap.put(key, val);
    }

    public void deleteOldestKey() {
        /**
         * 列表頭部就是最久未使用的
         */
        int key = linkedHashMap.keySet().iterator().next();
        linkedHashMap.remove(key);
    }

}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章