Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

146.LRU (最近最少使用) 缓存机制 #10

Open
wtfjun opened this issue Oct 18, 2018 · 2 comments
Open

146.LRU (最近最少使用) 缓存机制 #10

wtfjun opened this issue Oct 18, 2018 · 2 comments

Comments

@wtfjun
Copy link
Owner

wtfjun commented Oct 18, 2018

leetcode 146. LRU (最近最少使用) 缓存机制

题目描述

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

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

进阶:

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

示例:

LRUCache cache = new LRUCache( 2 /* 缓存容量 */ );

cache.put(1, 1);
cache.put(2, 2);
cache.get(1);       // 返回  1
cache.put(3, 3);    // 该操作会使得密钥 2 作废
cache.get(2);       // 返回 -1 (未找到)
cache.put(4, 4);    // 该操作会使得密钥 1 作废
cache.get(1);       // 返回 -1 (未找到)
cache.get(3);       // 返回  3
cache.get(4);       // 返回  4

解题思路:

搞清楚两个问题就行,执行put操作的时候关注

一、缓存数据的变化

分为两种情况:

1.缓存不满

命中缓存(缓存中存在该值),则缓存无任何变化

未命中缓存(缓存中不存在该值),缓存中加入该值

2.缓存已满

命中缓存,缓存无变化

未命中缓存,删掉缓存末尾的值,之后缓存中加入该值

从以上分析,要想找到缓存末尾的值,我想到两个办法。

(1)将缓存有序化(有序化涉及到排序,增加算法复杂度,所以我不用这个方法)

(2)设置一个指针从内存第一个数开始跟踪缓存末尾的值

二、内存中增加数据时内存的变化

也是两种情况
1.内存中不存在该值(新数据)

直接将该值置于内存首部

2.内存中已经存在该值(旧数据)

更新内存中值的顺序,规则是将改值的前一个节点的下一个节点设置为该值的下一个节点,然后该值置于内存首部(基本链表操作)

这里需要考虑部分特殊情况,比如内存为空的情况下连续执行以下操作

put(1, 1);
put(1, 1);

所以更新的规则要兼容以上情况

执行get的时候,如果缓存中存在get的数据,则更新缓存顺序,跟以上一样。

代码:

let LRUCache = function(capacity) {
    this.cacheSize = capacity;
    // 缓存计数器
    this.cacheIndex = 0;
    this.cacheSet = new Set();
    // 内存头节点
    this.head = null;
    // 缓存尾节点
    this.cacheShift = null;
    this.memory = {};
};

LRUCache.prototype.get = function(key) {
    let val;
    const { cacheSet, memory } = this;
    if (cacheSet.has(key)) {
        val = memory[key].value;
        console.log(memory[key].value)
        // get 最后一个节点
        if (memory[key].next == null) {
            return val;
        }
        if (memory.cacheShift === memory[key] && memory.cacheShift.next) {
            memory.cacheShift = memory.cacheShift.next;
        }
        this.memorySort(key);
    } else {
        val = -1;
        console.log(-1);
    }
    
    return val;
};

LRUCache.prototype.put = function(key, value) {
    const { cacheSet, memory } = this;

    if (this.cacheIndex < this.cacheSize) {
        !cacheSet.has(key) && this.cacheIndex++;
        cacheSet.add(key)
    } else {
        if (!cacheSet.has(key)) {
            cacheSet.delete(memory.cacheShift.key);
            memory.cacheShift.next && (memory.cacheShift = memory.cacheShift.next);
            cacheSet.add(key);
        }
    }

    // 内存中有值
    if (memory.head) {
        // 内存中不存在该节点
        if (!memory[key]) {
            memory[key] = {
                prev: memory.head,
                next: null
            }
            memory.head.next = memory[key];
            memory.head = memory[key];
        } else { // 内存中存在节点
            if (memory.cacheShift === memory[key] && memory.cacheShift.next) {
                memory.cacheShift = memory[key].next;
            }
            this.memorySort(key);
        }
    } else {  // 内存为空,该节点为第一个节点
        memory[key] = {
            prev: null,
            next: null
        };
        memory.cacheShift = memory.head = memory[key];
    }

    memory[key].key = key;
    memory[key].value = value;
};

LRUCache.prototype.memorySort = function(key) {
    const { memory } = this;
    // get 的不是最后一个节点
    if (memory[key].next != null) {
        if (memory[key].prev != null) {
            memory[key].prev.next = memory[key].next;
        } else {    // 第一个节点
            memory[key].next.prev = null;
        }
        memory[key].next.prev = memory[key].prev;
        memory[key].prev = memory.head;
        memory[key].next = null;
        memory.head.next = memory[key];
        memory.head = memory[key];
    } 
}
@wtfjun wtfjun changed the title LRU (最近最少使用) 缓存机制 146.LRU (最近最少使用) 缓存机制 Oct 19, 2018
@wtfjun wtfjun mentioned this issue Nov 2, 2018
@wtfjun wtfjun added the 中等 label Nov 13, 2018
@Antogetherle
Copy link

Antogetherle commented Jan 4, 2019

我对数据结构不熟悉,请教下,以下的写法有何不妥。

var LRUCache = function(capacity) {
    this.capacity = capacity;
    this.storage = {};
    this.keyOrder = [];
};

LRUCache.prototype.get = function(key) {
	let val = this.storage[key];

	//	取值的key不在队头,调整到队头
	if( val!==undefined && this.keyOrder[0] !== key ){
		let pos = this.keyOrder.indexOf(key);
		this.keyOrder.splice(pos,1);
		this.keyOrder.unshift(key);
	}
	val =  val  === undefined ? -1 :  val;

	console.warn( {op:'get',key,val,keyOrder:this.keyOrder.join(),storage:this.storage} )
        return val;
};


LRUCache.prototype.put = function(key, value) {

    //	如果已存在,直接删除清理
    if( this.storage[key] !== undefined ){
    	let pos = this.keyOrder.indexOf(key);
		this.keyOrder.splice(pos,1);
		delete this.storage[key];
    }

	//	长度检查清理,清理队尾
    if( this.keyOrder.length >= this.capacity ){
    	let popKey = this.keyOrder.pop();
    	delete this.storage[popKey];
    }

    //	在队头插入
    this.keyOrder.unshift(key);
    this.storage[key] = value;

    console.warn( {op:'set',key,value,keyOrder:this.keyOrder.join(),storage:this.storage} )
};

@wtfjun
Copy link
Owner Author

wtfjun commented Jan 9, 2019

@Antogetherle 首先,涉及到数组的操作 splice、indexOf等等,你要考虑他的复杂度。尽量满足O(1)的要求,往O(1)的角度去解决。我没仔细看你这个解法,但是里面有好多操作达不到O(1)的要求,可以去看一下算法复杂度这方面的知识。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants