面试题:实现LRUCache::Least Recently Used的缩写,意思是最近最少使用,它是一种Cache替换算法

Design and implement a data structure for Least Recently Used (LRU) cache. It should support the following operations: get and set.

get(key) - Get the value (will always be positive) of the key if the key exists in the cache, otherwise return -1.
set(key, value) - Set or insert the value if the key is not already present. When the cache reached its capacity, it should invalidate the least recently used item before inserting a new item.

  • 题目大意:为LRU Cache设计一个数据结构,它支持两个操作:

   1)get(key):如果key在cache中,则返回对应的value值,否则返回-1

   2)set(key,value):如果key不在cache中,则将该(key,value)插入cache中(注意,如果cache已满,则必须把最近最久未使用的元素从cache中删除);如果key在cache中,则重置value的值。

  • 解题思路:题目让设计一个LRU Cache,即根据LRU算法设计一个缓存。在这之前需要弄清楚LRU算法的核心思想,LRU全称是Least

Recently Used,即最近最久未使用的意思。在操作系统的内存管理中,有一类很重要的算法就是内存页面置换算法(包括FIFO,LRU,LFU等几种常见页面置换算法)。事实上,Cache算法和内存页面置换算法的核心思想是一样的:都是在给定一个限定大小的空间的前提下,设计一个原则如何来更新和访问其中的元素。下面说一下LRU算法的核心思想,LRU算法的设计原则是:如果一个数据在最近一段时间没有被访问到,那么在将来它被访问的可能性也很小。也就是说,当限定的空间已存满数据时,应当把最久没有被访问到的数据淘汰。

  而用什么数据结构来实现LRU算法呢?可能大多数人都会想到:用一个数组来存储数据,给每一个数据项标记一个访问时间戳,每次插入新数据项的时候,先把数组中存在的数据项的时间戳自增,并将新数据项的时间戳置为0并插入到数组中。每次访问数组中的数据项的时候,将被访问的数据项的时间戳置为0。当数组空间已满时,将时间戳最大的数据项淘汰。

  这种实现思路很简单,但是有什么缺陷呢?需要不停地维护数据项的访问时间戳,另外,在插入数据、删除数据以及访问数据时,时间复杂度都是O(n)。

  那么有没有更好的实现办法呢?

  那就是利用链表和hashmap。当需要插入新的数据项的时候,如果新数据项在链表中存在(一般称为命中),则把该节点移到链表头部,如果不存在,则新建一个节点,放到链表头部,若缓存满了,则把链表最后一个节点删除即可。在访问数据的时候,如果数据项在链表中存在,则把该节点移到链表头部,否则返回-1。这样一来在链表尾部的节点就是最近最久未访问的数据项。

  总结一下:根据题目的要求,LRU Cache具备的操作:

  1)set(key,value):如果key在hashmap中存在,则先重置对应的value值,然后获取对应的节点cur,将cur节点从链表删除,并移动到链表的头部;若果key在hashmap不存在,则新建一个节点,并将节点放到链表的头部。当Cache存满的时候,将链表最后一个节点删除即可。

  2)get(key):如果key在hashmap中存在,则把对应的节点放到链表头部,并返回对应的value值;如果不存在,则返回-1。

package com.Netesay.interview;

import java.util.HashMap;
import java.util.Map;

/**
 * @Author: weblee
 * @Email: [email protected]
 * @Blog: http://www.cnblogs.com/lkzf/
 * @Time: 2014年10月24日下午6:29:40
 *
 *************        function description ***************
 *
 ****************************************************
 */

public class LRUCache {
    Map<Integer, CacheNode> cacheMap;
    CacheNode head, tail;
    int capacity;

    //使用双向链表和map,map将k对应与链表的节点
    //链表里保存k和value
    public LRUCache(int capacity) {
    this.capacity = capacity;

    cacheMap = new HashMap<Integer, CacheNode>(capacity);

    head = new CacheNode(-1, -1);
    tail = new CacheNode(1, 1);

    head.next = tail;
    tail.pre = head;
    }

    public int get(int key) {
    if (cacheMap.containsKey(key)) {
        CacheNode node = (CacheNode)cacheMap.get(key);

        put2Head(node);

        return node.value;
    } else {
        return -1;
    }
    }

    public void set(int key, int value) {
    if (cacheMap.containsKey(key)) {
        CacheNode p = cacheMap.get(key);

        p.value = value;

        put2Head(p);
    } else if(cacheMap.size() < capacity) {
        CacheNode node = new CacheNode(key, value);
        put2Head(node);
        cacheMap.put(key, node);
    } else {
        CacheNode p = new CacheNode(key, value);
        put2Head(p);
        cacheMap.put(key, p);

        int tmpKey = removeEnd();
        cacheMap.remove(tmpKey);
    }
    }

    private void put2Head(CacheNode p) {
    if (p.next != null && p.pre != null) {
        p.pre.next = p.next;
        p.next.pre = p.pre;
    }

    p.pre = head;
    p.next = head.next;
    head.next.pre = p;
    head.next = p;
    }

    private int removeEnd() {
    CacheNode p = tail.pre;
    tail.pre.pre.next = tail;
    tail.pre = p.pre;

    p.pre = null;
    p.next = null;

    return p.key;
    }
}

class CacheNode {
    int key;
    int value;

    CacheNode pre;
    CacheNode next;

    public CacheNode(int key, int value) {
    this.key = key;
    this.value = value;
    }
}
时间: 2024-11-09 08:20:11

面试题:实现LRUCache::Least Recently Used的缩写,意思是最近最少使用,它是一种Cache替换算法的相关文章

lettcode 上的几道哈希表与链表组合的数据结构题

目录 LRU缓存 LFU缓存 全O(1)的数据结构 下面这几道题都要求在O(1)时间内完成每种操作. LRU缓存 LRU是Least Recently Used的缩写,即最近最少使用,是一种常用的页面置换算法,选择最近最久未使用的页面予以淘汰.该算法赋予每个页面一个访问字段,用来记录一个页面自上次被访问以来所经历的时间 t,当须淘汰一个页面时,选择现有页面中其 t 值最大的,即最近最少使用的页面予以淘汰. 做法: 使用先进先出的队列,队尾的元素即是可能要淘汰的. 由于需要查找某个key在队列中的

JavaScript算法题实现-146-LRU缓存机制——腾讯面试题库

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

LRU算法原理解析

LRU是Least Recently Used的缩写,即最近最少使用,常用于页面置换算法,是为虚拟页式存储管理服务的. 现代操作系统提供了一种对主存的抽象概念虚拟内存,来对主存进行更好地管理.他将主存看成是一个存储在磁盘上的地址空间的高速缓存,在主存中只保存活动区域,并根据需要在主存和磁盘之间来回传送数据.虚拟内存被组织为存放在磁盘上的N个连续的字节组成的数组,每个字节都有唯一的虚拟地址,作为到数组的索引.虚拟内存被分割为大小固定的数据块虚拟页(Virtual Page,VP),这些数据块作为主

使用C++实现一个LRU cache

什么是LRU Cache LRU是Least Recently Used的缩写,意思是最近最少使用,它是一种Cache替换算法.什么是Cache?狭义的Cache指的是位于CPU和主存间的快速RAM,通常它不像系统主存那样使用DRAM技术,而使用昂贵但较快速的SRAM技术.广义上的Cache指的是位于速度相差较大的两种硬件之间,用于协调两者数据传输速度差异的结构.除了CPU与主存之间有Cache,内存与硬盘之间也有Cache,乃至在硬盘与网络之间也有某种意义上的Cache──称为Internet

如何用C++实现一个LRU Cache

什么是LRU Cache LRU是Least Recently Used的缩写,意思是最近最少使用,它是一种Cache替换算法. 什么是Cache?狭义的Cache指的是位于CPU和主存间的快速RAM, 通常它不像系统主存那样使用DRAM技术,而使用昂贵但较快速的SRAM技术. 广义上的Cache指的是位于速度相差较大的两种硬件之间, 用于协调两者数据传输速度差异的结构.除了CPU与主存之间有Cache, 内存与硬盘之间也有Cache,乃至在硬盘与网络之间也有某种意义上的Cache── 称为In

LRU Cache的简单实现

Cache这个东西可以说无处不在,处理器中的TLB,Linux系统中的高速页缓存,还有很多人熟知的开源软件memcached,都是cache的一种实现.LRU是Least Recently Used的缩写,即最近最少使用,是常用cache算法中的一种.因为cache的存储空间相对于后端存储来说更有限,将cache空间和后端存储空间映射后,还需要一些算法来解决cache满的问题并保证效率,LRU就是在cache满了以后,将最近最少访问到的内容移除,然后将新的内容放入cache,新的内容也成为了最近

java数据结构之LinkedHashMap

一.源码注释 public class LinkedHashMap<K,V> extends HashMap<K,V> implements Map<K,V> { /** * LinkedHashMap的节点类,在HashMap的节点的基础上增加了指向前一个节点和后一个节点的属性,来构成双向链表 */ static class Entry<K,V> extends HashMap.Node<K,V> { Entry<K,V> befo

Redis额内存回收策略和内存上限

内存上限Redis可以通过 maxmemory 参数来限制最大可用内存,主要为了避免Redis内存超过操作系统内存,从而导致服务器响应变慢甚至死机的情况. maxmemory 参数限制的是Redis的对象内存大小,也就是 used_memory 对应的内存大小.由于内存碎片的存在,所以Redis服务器实际占用的内存是要超过 maxmemory 的. 所以我们在设置Redis内存上限的时候要预留一部分内存出来,比如说一台32GB内存的机器,可以启动 3 台8GB内存的Redis,预留8GB给机器其

Android 面试题集 包含答案

作者:guoxiaoxing 链接: https://github.com/guoxiaoxing/android-interview 本文基于作者采用的MIT协议分发. 手画一下Android系统架构图,描述一下各个层次的作用? Android系统架构图 从上到下依次分为六层: 应用框架层 进程通信层 系统服务层 Android运行时层 硬件抽象层 Linux内核层 Activity如与Service通信? 可以通过bindService的方式,先在Activity里实现一个ServiceCo