• LRU 缓存机制


      因为希望是O(1)的时间复杂度,所以很容易想到需要使用哈希表。那么接下来,就直接讲实现思路了。
      LRUCache 的常见实现方式是:哈希表+双向链表。那为什么不是哈希表+数组了。因为数组的查找和替换是O(N)级别的,所以需要使用双向链表。

    思路:

     

    说明:

      1. map用来作为存储容器,key是传进来的Int值valueNode节点;即map[int] = node

      2. Node节点信息包含


        {keyvalueprev:Nodenext:Node

          3.LRUCache类包含:map, 以及first , last2 个虚拟的节点

          图中的first节点和last的节点都是虚节点,不存储任何值,只是为了维持双向链表的完整性。

    完成这个LRUCache的核心思路是:在存和取的时候更新节点的位置。

    • 如果该节点存在,执行以下3步
    • 如果该节点不存在
    • 判断是否已经达到最大容量

    1. 如果没有取到值,就直接返回-1
    2. 如果取到值了,就需要更新这个节点的位置
    3. 我们需要将它更新到first节点的后面。具体的执行步骤有以下2步
    4. 删除该结点,【也就是第5个节点】
    5. 把该结点添加到first节点的后面

    1. 存的时候,先去根据key获取该结点信息
    2. 将其value的值更新一下
    3. 删除该结点
    4. 并且将该节点添加到first节点的后面
    5. 如果不是,就将该结点添加到first节点后面
    6. 如果是,就将最后一个节点【last的prev节点】删掉,然后将新节点添加到fisrt节点的后面

    代码如下:swift版本和java版本

    class LRUCache { //【swift版本】
        private var map = [Int : Node]()
        private var capacity = 0
        private let first = Node(0, 0)
        private let last = Node(0, 0)
        
        init(_ capacity: Int) {
            self.capacity = capacity
            // 初始化指向
            first.next = last
            last.prev = first
        }
        
        func get(_ key: Int) -> Int {
            guard let node = map[key] else { return -1 }
            // 如果取到值了
            //1.先将该结点删除掉,但是map不删
            removeNode(node: node)
            //2.就将之插入到first节点的后面
            insertToFirstBehind(node: node)
            return node.value
        }
        
        func put(_ key: Int, _ value: Int) {
            
            guard let node = map[key] else {
                // 添加一对新的key-value
                if map.count == capacity {
                    // 淘汰最近最少使用的 即删掉last节点前的那个节点,并且map也删
                    let needDeleteNode = map.removeValue(forKey: last.prev!.key)!
                    removeNode(node: needDeleteNode)
                }
                // 然后添加
                let node = Node(key, value)
                map[key] = node
                insertToFirstBehind(node: node)
                
                
                return
            }
            // 值不为空就更新下值
            node.value = value
            removeNode(node: node)
            insertToFirstBehind(node: node)
        }
        
        private func removeNode(node: Node) {
            node.next?.prev = node.prev
            node.prev?.next = node.next
        }
        
        private func insertToFirstBehind(node: Node) {
            // node 和 first.next
            first.next?.prev = node
            node.next = first.next
            //node 和 first
            first.next = node
            node.prev = first
        }
        
        class Node {
            public let key: Int
            public var value : Int
            public var prev: Node?
            public var next: Node?
            init(_ key: Int, _ value: Int) {
                self.key = key
                self.value = value
            }
        }
    }
    public class LRUCache { //【java版本】
        private Map<Integer, Node> map;
        private int capacity;
        // 虚拟头结点
        private Node first;
        // 虚拟尾结点
        private Node last;
     
        public LRUCache(int capacity) {
            map = new HashMap<>(capacity);
            this.capacity = capacity;
            first = new Node();
            last = new Node();
            first.next = last;
            last.prev = first;
        }
     
        public int get(int key) {
            Node node = map.get(key);
            if (node == null) return -1;
     
            removeNode(node);
            addAfterFirst(node);
     
            return node.value;
        }
     
        /**
         * @param node 将node节点插入到first节点的后面
         */
        private void addAfterFirst(Node node) {
            // node与first.next
            node.next = first.next;
            first.next.prev = node;
     
            // node与first
            first.next = node;
            node.prev = first;
        }
     
        /**
         * @param node 从双向链表中删除node节点
         */
        private void removeNode(Node node) {
            node.next.prev = node.prev;
            node.prev.next = node.next;
        }
     
        public void put(int key, int value) {
            Node node = map.get(key);
            if (node != null) {
                node.value = value;
                removeNode(node);
            } else { // 添加一对新的key-value
                if (map.size() == capacity) {
                    // 淘汰最近最少使用的node
                    removeNode(map.remove(last.prev.key));
    //                map.remove(last.prev.key);
    //                removeNode(last.prev);
                }
                map.put(key, node = new Node(key, value));
            }
            addAfterFirst(node);
        }
     
        private static class Node {
            public int key;
            public int value;
            public Node prev;
            public Node next;
            public Node(int key, int value) {
                this.key = key;
                this.value = value;
            }
            public Node() {}
        }
    }

    总结
      今天主要讲了LRU缓存的实现思路,以及具体的代码实现。其核心就是在存取的时候完成整个LRU算法的一个运转,保持最先最少使用的被淘汰,然后一直运转下去。使用的数据结构是大家比较熟悉的字典和双向链表。希望能帮助到大家。

    欢迎关注【无量测试之道】公众号,回复【领取资源】
    Python编程学习资源干货、
    Python+Appium框架APP的UI自动化、
    Python+Selenium框架Web的UI自动化、
    Python+Unittest框架API自动化、
    资源和代码 免费送啦~
    文章下方有公众号二维码,可直接微信扫一扫关注即可。

    备注:我的个人公众号已正式开通,致力于测试技术的分享,包含:大数据测试、功能测试,测试开发,API接口自动化、测试运维、UI自动化测试等,微信搜索公众号:“无量测试之道”,或扫描下方二维码:


    添加关注,让我们一起共同成长!

  • 相关阅读:
    asp.net core系列 26 EF模型配置(实体关系)
    asp.net core系列 25 EF模型配置(隐藏属性)
    asp.net core系列 24 EF模型配置(主键,生成值,最大长度,并发标记)
    (办公)TOKEN
    (办公)plug-in org.eclipse.jdt.ui was unable to load class org.eclipse.jdt.internal
    (办公)系统死锁了.
    (办公)MojoExecutionException
    (生活)Photoshop入门(不定时更新)
    (办公)百度api的使用
    (办公)Mysql入门
  • 原文地址:https://www.cnblogs.com/Wu13241454771/p/15157462.html
Copyright © 2020-2023  润新知