美文网首页
LeetCode--146. LRU缓存机制(未优化时间复杂度)

LeetCode--146. LRU缓存机制(未优化时间复杂度)

作者: sjandroid | 来源:发表于2020-03-31 16:44 被阅读0次

题目:

运用你所掌握的数据结构,设计和实现一个 LRU (最近最少使用) 缓存机制。它应该支持以下操作: 获取数据 get 和 写入数据 put 。

获取数据 get(key) - 如果密钥 (key) 存在于缓存中,则获取密钥的值(总是正数),否则返回 -1。
写入数据 put(key, value) - 如果密钥不存在,则写入其数据值。当缓存容量达到上限时,它应该在写入新数据之前删除最久未使用的数据值,从而为新的数据值留出空间。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/lru-cache


LRU原理与理解

待更新...


实现

public class LruIntegerCache {
    //最大容量
    private int mMaxCapacity;
    //当前容量
    private int mCurCapacity;

    //头结点
    private Node mHead = new Node("Head");
    //尾巴结点
    private Node mTail = new Node("Tail");

    public LruIntegerCache(int maxCapacity) {
        mMaxCapacity = maxCapacity;
        init();
    }

    private void init() {
        //构造最初链表head-->tail
        mHead.next = mTail;
        mTail.pre = mHead;
    }

    /**
     * 获取数据
     * 1:遍历链表,查找是否有key只想的数据
     * 2:没有数据,返回默认值。因为此类专门存储是的int类型数据所以返回-1
     * 2:有数据,把此数据【添加到链表首部】
     *
     * @param key key
     * @return value
     */
    public int get(int key) {
        boolean hasFlag = false;

        Node nextNode = mHead.next;
        //遍历链表(优化遍历:为了加快查找速度,可以分别从表头和表尾同时开始遍历此链表)
        while (null != nextNode && nextNode != mTail) {
            if (nextNode.key == key) {
                hasFlag = true;
                break;
            }
            nextNode = nextNode.next;
        }
        //有数据
        //1:先把当前节点从链表中移除
        //2:把当前节点添加到'首节点'处
        if (hasFlag) {
            delete(nextNode);
            addToFirst(nextNode);
            return nextNode.value;
        }
        //无数据
        return -1;
    }

    /**
     * 存数据
     * 1:先判断是否有key所对应的数据
     * <p>
     * 2:有
     * 2.1:更新value
     * 2.2:把此节点添加到链表首部
     * <p>
     * 3:没有,判断如果保存此数据后,是否会达到容量限制
     * 3.1:没超过,则把把key,value 构造成一个新节点,添加到链表首部
     * 3.2:超过容量限制,从表尾巴开始删除,直到容量可以保存下新数据为止
     *
     * @param key   key
     * @param value value
     */
    public void put(int key, int value) {
        Node node = getNode(key);
        if (node != null) {
            update(node, value);
        } else {
            if (isOverSize(sizeOf(key))) {
                //delete
                deleteFromTail();
            }
            addToFirst(key, value);
        }
    }

    /**
     * 把待添加节点添加到链表首部
     *
     * @param newNode 待添加节点
     */
    private void addToFirst(Node newNode) {
        mCurCapacity += sizeOf(newNode.key);

        Node fistNode = mHead.next;

        mHead.next = newNode;
        newNode.pre = mHead;

        fistNode.pre = newNode;
        newNode.next = fistNode;
    }

    /**
     * 把 key,value构造成一个节点,添加到链表首部
     *
     * @param key   key
     * @param value vaalue
     */
    private void addToFirst(int key, int value) {
        int sizeOf = sizeOf(key);
        mCurCapacity += sizeOf;

        Node newNode = new Node(key, value, sizeOf);
        Node fistNode = mHead.next;

        mHead.next = newNode;
        newNode.pre = mHead;

        fistNode.pre = newNode;
        newNode.next = fistNode;
    }

    /**
     * 更新链表中节点的 value
     * <p>
     * 1:更新前查看,更新后的内容是否会超过容量限制
     * <p>
     * 2:没超过
     * 2.1:更新 value
     * 2.2:把当前节点添加到链表首部
     * <p>
     * 3:超过
     * 3.1:从链表尾部开始删除数据
     * 3.2:从链表中删除此接单,并把节点天机到此链表首部
     *
     * @param node
     * @param value
     */
    private void update(Node node, int value) {
        int sizeOf = sizeOf(node.key);
        if (!isOverSize(sizeOf, node.valueSize)) {
            //更新value
            node.value = value;
            node.valueSize = sizeOf;
        } else {
            //从tail开始删除数据直到能存下更新的数据为止
            deleteFromTail();
        }
        //删除当前node
        delete(node);
        //移动的链表头部
        addToFirst(node);
    }

    /**
     * 从链表中删除给定的节点
     *
     * @param deleteNode 待删除链表
     */
    private void delete(Node deleteNode) {
        Node preNode = deleteNode.pre;
        Node nextNode = deleteNode.next;

        deleteNode.pre = null;
        deleteNode.next = null;

        preNode.next = nextNode;
        nextNode.pre = preNode;

        mCurCapacity -= deleteNode.valueSize;
    }

    /**
     * 当当前容量超过最大容量是,从链表尾部删除节点
     * 该方法在使用前需要借助 {@link com.sj.android_custom_view.test.LruIntegerCache#isOverSize(int)}判断下当前容量
     */
    private void deleteFromTail() {
        Node deleteNode = mTail.pre;

        while (deleteNode != null && deleteNode != mHead) {
            Node preNode = deleteNode.pre;
            //删除node
            preNode.next = deleteNode.next;
            deleteNode.next.pre = preNode;

            int deleteSizeOf = deleteNode.valueSize;
            mCurCapacity -= deleteSizeOf;

            if (mCurCapacity > mMaxCapacity) {
                deleteNode = preNode;
                continue;
            }
            return;
        }
    }

    private Node getNode(int key) {
        Node lastNode = mHead.next;
        while (lastNode != null && lastNode != mTail) {
            if (lastNode.key == key) {
                return lastNode;
            }
            lastNode = lastNode.next;
        }
        return null;
    }

    /**
     * 跟新新添加节点的 value所占的实际大小,判断容量是否超过最大限制
     *
     * @param sizeOf 待添加数据的大小
     * @return
     */
    private boolean isOverSize(int sizeOf) {
        return mCurCapacity + sizeOf > mMaxCapacity;
    }

    private boolean isOverSize(int newSizeOf, int oldSizeOf) {
        return mCurCapacity - oldSizeOf + newSizeOf > mMaxCapacity;
    }

    /**
     * 当前key所对应value的实际大小
     *
     * @param key key
     * @return value的实际大小
     */
    protected int sizeOf(int key) {
        return 1;
    }

    public String raversetFromHead() {
        final String splteStr = "-->";
        StringBuilder sb = new StringBuilder();

        Node lastNode = mHead.next;
        while (lastNode != null && lastNode != mTail) {
            sb.append(lastNode.key);
            sb.append(splteStr);

            lastNode = lastNode.next;
        }
        sb.delete(sb.length() - splteStr.length(), sb.length());

        return sb.toString();
    }

    public String raversetFromTail() {
        final String splteStr = "-->";
        StringBuilder sb = new StringBuilder("Count:" + mCurCapacity + ":");

        Node preNode = mTail.pre;
        while (preNode != null && preNode != mHead) {
            sb.append(preNode.key);
            sb.append(splteStr);

            preNode = preNode.pre;
        }
        sb.delete(sb.length() - splteStr.length(), sb.length());

        return sb.toString();
    }

    private static class Node {
        private int key;
        private int value;

        private Node pre;
        private Node next;

        private String tag;
        private int valueSize;

        private Node(String tag) {
            this.tag = tag;
        }

        private Node(int key, int value, int valueSize) {
            this.key = key;
            this.value = value;
            this.valueSize = valueSize;
        }
    }
}

相关文章

网友评论

      本文标题:LeetCode--146. LRU缓存机制(未优化时间复杂度)

      本文链接:https://www.haomeiwen.com/subject/cjxguhtx.html