LeetCode146 动手实现LRU算法

栏目: 编程工具 · 发布时间: 6年前

内容简介:运用你所掌握的数据结构,设计和实现一个获取数据写入数据

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

解题思路

  • LRU是Least Recently Used的缩写,即"最近最少使用",也就是说,LRU缓存把最近最少使用的数据移除,让给最新读取的数据
  • 往往最常读取的,也是读取次数最多的
  • 操作系统等最常使用的缓存策略,即LRU
  • 需要在O(1)时间复杂度实现put操作,就需要使用双向链表,方便移动节点(单链表无法达到O(1))
  • O(1)实现get查询操作,就需要使用map存key-node键值对,实现快速查询
  • 具体详见代码注释

代码实现

// doublyLinkedNode defines a node for double-linked-list
type doublyLinkedNode struct {
    prev, next *doublyLinkedNode
    key, val   int
}

// LRUCache defines a object for cache
type LRUCache struct {
    len, cap    int
    first, last *doublyLinkedNode         //head,tail
    nodes       map[int]*doublyLinkedNode //hashtable,find node in O(1)
}

// Constructor creates a cache object
func Constructor(capacity int) LRUCache {
    return LRUCache{
        len:   0,
        cap:   capacity,
        first: nil,
        last:  nil,
        nodes: make(map[int]*doublyLinkedNode, capacity),
    }
}

// Get returns value by key
func (l *LRUCache) Get(key int) int {
    if node, ok := l.nodes[key]; ok {
        //key exist
        // move the node to the head of double-linked-list
        l.moveToFirst(node)
        return node.val
    }

    //key not exist,return -1
    return -1
}

// Put puts key-value pair into LRUCache
func (l *LRUCache) Put(key int, value int) {
    if node, ok := l.nodes[key]; ok {
        //update value of old node
        node.val = value
        // move the node to the head of double-linked-list
        l.moveToFirst(node)
    } else {
        if l.len == l.cap {
            delete(l.nodes, l.last.key)
            l.removeLast()
        } else {
            l.len++
        }
        node := &doublyLinkedNode{
            prev: nil,
            next: nil,
            key:  key,
            val:  value,
        }
        l.nodes[key] = node
        l.insertToFirst(node)
    }
}

func (l *LRUCache) removeLast() {
    if l.last.prev != nil {
        //双向链表长度>1
        l.last.prev.next = nil
    } else {
        //双向链表长度 == 1,first == last
        l.first = nil
    }
    l.last = l.last.prev
}
func (l *LRUCache) moveToFirst(node *doublyLinkedNode) {
    switch node {
    case l.first:
        return
    case l.last:
        l.removeLast()
    default:
        //在双向链中,删除 node 节点
        node.prev.next = node.next
        node.next.prev = node.prev
    }
    // 策略是
    // 如果是移动node
    // 先删除,再插入
    l.insertToFirst(node)
}

func (l *LRUCache) insertToFirst(node *doublyLinkedNode) {
    if l.last == nil {
        //空的双向链表
        l.last = node
    } else {
        node.next = l.first
        l.first.prev = node
    }
    l.first = node
}

GitHub

  • 源码在这里
  • 项目中会提供各种数据结构及算法的Golang实现,以及 LeetCode 解法

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

SEO深度解析

SEO深度解析

痞子瑞 / 电子工业出版社 / 2014-3-1 / CNY 99.00

《SEO深度解析》以SEO从业人员普遍存在的疑问、经常讨论的问题、容易被忽视的细节以及常见的错误理论为基础,对SEO行业所包含的各方面内容进行了深入的讨论,使读者更加清晰地了解SEO及操作思路。内容分为两类:一类为作者根据自己真实、丰富的SEO经验对SEO所涉及的各种问题进行详细的讨论,主要包括SEO 基础原理剖析、SEO实操思路方法、常用工具数据剖析、竞争对手分析案例实操、网站数据分析思路指导、......一起来看看 《SEO深度解析》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

MD5 加密
MD5 加密

MD5 加密工具

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换