zoukankan      html  css  js  c++  java
  • LeetCode146-LRU缓存机制

    题目描述

    运用你所掌握的数据结构,设计和实现一个  LRU (最近最少使用) 缓存机制。它应该支持以下操作: 获取数据 get 和 写入数据 put 。
    获取数据 get(key) - 如果密钥 (key) 存在于缓存中,则获取密钥的值(总是正数),否则返回 -1。
    写入数据 put(key, value) - 如果密钥已经存在,则变更其数据值;如果密钥不存在,则插入该组「密钥/数据值」。当缓存容量达到上限时,它应该在写入新数据之前删除最久未使用的数据值,从而为新的数据值留出空间。

    进阶:
    你是否可以在 O(1) 时间复杂度内完成这两种操作?

    分析

    这个题目的分析感觉很有意思,记录下这个思维过程。

    一看键值对就知道使用Map来存储数据。
    关键怎么实现LRU(Least Resently Use)这个功能?
    首先明确一点:我们要记录一个使用记录表————这个表里面的数据应该有两个特性:
    1.无重复
    2.按使用先后顺序排列

    这样我们就能找出最近最少使用的那个元素。

    现在问题来了:
    1.我们使用什么数据结构来存放这个使用记录表
    2.getput是怎么调整这个表使之具有无重且有序?
    显然问题1是核心问题。

    考虑数据结构:
    1.Set:满足无重复数据,但是顺序不能满足
    2.Queue:队列有FIFO的特性,比较合适该场景。队头可以表示最近最少使用的,队尾是刚使用的。get的时候把队列里面的该元素移到到队尾,put的时候如果map存在该元素,更新并在队列中把该元素移动到末尾······ 问题来了,队列中元素的查找需要O(N)的时间,效率不高。
    3.栈:不适合
    4.链表:链表和队列一样查询元素也需要O(N)的时间???
    5.都不太适合???

    打破思维定势:
    如果要实现O(1)的put、get操作复杂度,那么明显需要做一件事:O(1)时间内查询、更新该使用记录表
    首先我们不能使用索引:难以正确定位。
    实际上我们发现按值传递的数据传递形式都是行不通的,如何通过map中的value直接定位到该使用记录表中相应元素?
    答案是按引用传递,如果map中的value和使用记录表中的对应元素都是指向一个相同的地址,那么我们可以轻易地定位。
    那么我们需要打破思维定势,不是说K和V都是int型的我们就需要定义map<int,int>,这里我们需要定义map<int,Node>
    其中Node也应该是具有key、value的对象,这时我们发现使用链表最合适。

    从操作上看:
    我们可以定义一个链表,头表示最近最少使用的元素,尾表示刚使用的元素(二者相反也行)。
    get时:
    1.如果该Node存在于链表中,我们把它移动到尾部。
    2.如果不存在:返回-1
    put时:
    1.如果该Node存在,更新值并移动到队部
    2.如果不存在:创建新节点
    a.如果元素达到容量限制:将头节点移除,把新节点加到尾部
    b.如果元素没有达到容量限制:加入尾部。

    有移到到尾部,有删除头结点:链表还是双向链表???
    在操作中一个节点总是需要前继和后继节点,所以应该使用双向链表。

    代码实现

    public class LRUCache {
        class DLinkedNode {
            int key;
            int value;
            DLinkedNode prev;
            DLinkedNode next;
            public DLinkedNode() {}
            public DLinkedNode(int _key, int _value) {key = _key; value = _value;}
        }
    
        private Map<Integer, DLinkedNode> cache = new HashMap<Integer, DLinkedNode>();
        private int size;
        private int capacity;
        private DLinkedNode head, tail;
    
        public LRUCache(int capacity) {
            this.size = 0;
            this.capacity = capacity;
            // 使用伪头部和伪尾部节点
            head = new DLinkedNode();
            tail = new DLinkedNode();
            head.next = tail;
            tail.prev = head;
        }
    
        public int get(int key) {
            DLinkedNode node = cache.get(key);
            if (node == null) {
                return -1;
            }
            // 如果 key 存在,先通过哈希表定位,再移到头部
            moveToHead(node);
            return node.value;
        }
    
        public void put(int key, int value) {
            DLinkedNode node = cache.get(key);
            if (node == null) {
                // 如果 key 不存在,创建一个新的节点
                DLinkedNode newNode = new DLinkedNode(key, value);
                // 添加进哈希表
                cache.put(key, newNode);
                // 添加至双向链表的头部
                addToHead(newNode);
                ++size;
                if (size > capacity) {
                    // 如果超出容量,删除双向链表的尾部节点
                    DLinkedNode tail = removeTail();
                    // 删除哈希表中对应的项
                    cache.remove(tail.key);
                    --size;
                }
            }
            else {
                // 如果 key 存在,先通过哈希表定位,再修改 value,并移到头部
                node.value = value;
                moveToHead(node);
            }
        }
    
        private void addToHead(DLinkedNode node) {
            node.prev = head;
            node.next = head.next;
            head.next.prev = node;
            head.next = node;
        }
    
        private void removeNode(DLinkedNode node) {
            node.prev.next = node.next;
            node.next.prev = node.prev;
        }
    
        private void moveToHead(DLinkedNode node) {
            removeNode(node);
            addToHead(node);
        }
    
        private DLinkedNode removeTail() {
            DLinkedNode res = tail.prev;
            removeNode(res);
            return res;
        }
    }
    
  • 相关阅读:
    Unique Binary Search Trees 解答
    Unique Paths II 解答
    Unique Paths 解答
    Maximum Subarray 解答
    Climbing Stairs 解答
    House Robber II 解答
    House Robber 解答
    Valid Palindrome 解答
    Container With Most Water 解答
    Remove Duplicates from Sorted List II 解答
  • 原文地址:https://www.cnblogs.com/XT-xutao/p/12961350.html
Copyright © 2011-2022 走看看