首页 > 试题广场 >

设计LRU缓存结构

[编程题]设计LRU缓存结构
  • 热度指数:144640 时间限制:C/C++ 2秒,其他语言4秒 空间限制:C/C++ 256M,其他语言512M
  • 算法知识视频讲解
设计LRU(最近最少使用)缓存结构,该结构在构造时确定大小,假设大小为 k ,操作次数是 n ,并有如下两个功能
1. set(key, value):将记录(key, value)插入该结构
2. get(key):返回key对应的value值

提示:
1.某个key的set或get操作一旦发生,认为这个key的记录成了最常使用的,然后都会刷新缓存。
2.当缓存的大小超过k时,移除最不经常使用的记录。
3.输入一个二维数组与k,二维数组每一维有2个或者3个数字,第1个数字为opt,第2,3个数字为key,value
若opt=1,接下来两个整数key, value,表示set(key, value)
若opt=2,接下来一个整数key,表示get(key),若key未出现过或已被移除,则返回-1
对于每个opt=2,输出一个答案
4.为了方便区分缓存里key与value,下面说明的缓存里key用""号包裹

要求:set和get操作复杂度均为
数据范围:
示例1

输入

[[1,1,1],[1,2,2],[1,3,2],[2,1],[1,4,4],[2,2]],3

输出

[1,-1]

说明

[1,1,1],第一个1表示opt=1,要set(1,1),即将(1,1)插入缓存,缓存是{"1"=1}
[1,2,2],第一个1表示opt=1,要set(2,2),即将(2,2)插入缓存,缓存是{"1"=1,"2"=2}
[1,3,2],第一个1表示opt=1,要set(3,2),即将(3,2)插入缓存,缓存是{"1"=1,"2"=2,"3"=2}
[2,1],第一个2表示opt=2,要get(1),返回是[1],因为get(1)操作,缓存更新,缓存是{"2"=2,"3"=2,"1"=1}
[1,4,4],第一个1表示opt=1,要set(4,4),即将(4,4)插入缓存,但是缓存已经达到最大容量3,移除最不经常使用的{"2"=2},插入{"4"=4},缓存是{"3"=2,"1"=1,"4"=4}
[2,2],第一个2表示opt=2,要get(2),查找不到,返回是[1,-1]        
示例2

输入

[[1,1,1],[1,2,2],[2,1],[1,3,3],[2,2],[1,4,4],[2,1],[2,3],[2,4]],2

输出

[1,-1,-1,3,4]
前端面试直接用map会怎么样😂
发表于 2022-01-10 00:48:04 回复(1)
function LRU( operators ,  k ) {
    // write code here
    var memory = new Map()
    var ans = [];
    for(var opt of operators){
        switch(opt[0]){
            case 1:
                if(memory.size>=k){
                    var oKey = memory.keys().next().value;
                    memory.delete(oKey)
                }
                memory.set(opt[1], opt[2])
                break;
            case 2:
                if(memory.has(opt[1])){
                    var val = memory.get(opt[1])
                    memory.delete(opt[1])
                    memory.set(opt[1], val)
                    ans.push(val)
                }else{
                    ans.push(-1)
                }
                break;
            default:break;
        }
    }
    return ans
}
module.exports = {
    LRU : LRU
};

发表于 2022-01-04 10:28:58 回复(0)
function LRU( operators ,  k ) {
  // write code here
  let temp = new Map();
  let result = [];
  for(let i=0;i<operators.length;i++){
    if(operators[i][0]==1){
      if(temp.size==k){
        for(let key of temp.keys()){ 
          temp.delete(key);
          break;
        }
      }
      temp.set(operators[i][1], operators[i][2]);
    }else{
      if(temp.has(operators[i][1])){
        let m = temp.get(operators[i][1]);
        temp.delete(operators[i][1]);
        temp.set(operators[i][1], m);
        result.push(m);
      }else{
        result.push(-1);
      }
    }
  }
  return result;
}

发表于 2021-10-11 20:32:32 回复(0)
function LRU( operators ,  k ) {
    var obj=new Map()
    var ans=[]
    for(let item of operators){
        if(item[0]==1){
            obj.set(item[1],item[2])
        }
        else{
            var val=obj.get(item[1])
            if(!val)val=-1
            obj.delete(item[1])
            ans.push(val)
            if(val!=-1)obj.set(item[1],val)
        }
        if(obj.size>k){
            var arr=[...obj.keys()]
            obj.delete(arr[0])
        }
    }
    return ans
}


编辑于 2021-09-06 13:47:42 回复(0)
用js写的,没考虑时间复杂度,使用一个数组和一个map实现的。
原理和哈希+双向链表一样。我把双向链表用一个数组代替了
数组中存放最近使用的顺序,map存放键值对

   function LRU(operatorsk) {
      // write code here

      // 这个arr数组存放key,当做双向链表使用 
      // arr的头是最近使用过的,尾是最久未使用的
      let arr = []
      // res 存放最后返回的get结果
      let res = []
      let map = new Map()

      for (let i = 0; i < operators.length; i++) {
        // 如果是操作码是1,调用set
        if (operators[i][0== 1)
          set(operators[i][1], operators[i][2])
        // 操作码是2,调用get,并且把get返回结果存到res里
        else res[res.length] = get(operators[i][1])
      }
      return res

      function set(keyval) {
        if (get(key) > -1) {
          // 如果map里有这个数据,重新赋值
          // map.set(key,val)
          // 这里把set放到下面了,防止代码冗余
          // 另:js的map的set可以重新赋值,所以这条if里面什么都没有
        } {
          // 没有这个数,将其存入map和arr
          // 如果map已满,删除arr的最后一个元素,并将这个key值从map中删除
          if (map.size == k) {
            map.delete(arr.pop())
          }
        }
        // 将这个键值对存入map,重新赋值和第一次赋值都在这。arr中只存放key
        map.set(key, val)
        arr.unshift(key)
      }

      function get(key) {
        if (map.has(key)) {
          // 如果map里有这个值,把他提到arr首部
          // 获取索引
          let index = arr.indexOf(key)
          arr.unshift(arr[index])
          // 将这个key从原来的位置删除
          arr.splice(index + 11)
          return map.get(key)
        }
        return -1
      }

    }
发表于 2021-08-27 22:58:06 回复(0)
function LRU( operators ,  k ) {
    // write code here
  var mp=new Map();
  var len=operators.length;
  var r=[];
  for(var i=0;i<len;i++){
    var [cmd,...data]=operators[i];
    switch(cmd){
      case 1:
        var [key,val]=data;
        if(mp.has(key)){
          mp.delete(key);
        }else if(mp.size == k){
          var first_key=mp.keys().next().value;
          mp.delete(first_key);
        }
        mp.set(key,val)
       
        break;
      case 2:
        var [key]=data;
        if(mp.has(key)){
          var val=mp.get(key);
          mp.delete(key);
          mp.set(key,val);
          r.push(val);
        }else{
          r.push(-1)
        }
          
    }

  }
  return r;
}
发表于 2021-07-31 14:38:49 回复(0)
其实就是写两个对应的操作函数,一个很简单,就是给数组推值,如果超过的话,就移除最不常用的那个;另一个就是取值,但是取值之后需要对数组进行移位操作,将当前这个值设置为最常用的。
那么如何认定当前的对象是否是最常用的呢?一个情况是,每次为数组添加新的对象时,这个对象就为最常用的;另一个情况就是查询了这个对象,这个对象就是最常用的。
那么我们可以创建一个数组,认为这个数组的第0个位置是最常用的,末尾是最不常用的。每次添加对象都使用shift做头部插入。在添加对象之后都要查看是否超出了最大长度,如果超出,则移除最后一个对象。这是set的操作
而get的操作则相对复杂一些,每次做查询时,如果可以找到对应key的位置,那么就从当前位置将这个对象删除,并做一次头部插入,即更新最常用的设定;如果没找到key,则返回-1,在主函数中判断返回的key是否为-1,再推入不同的数据,因为这个时候已经将查询的对象放入头部,直接取0位置的value即可。
function set(record,key,value,k){
  record.unshift({key,value})//每次都从头部插入,作为最常用的位置
  //如果超过设定长度,则从尾部删除最不常用
  if(record.length>k){
    record.pop()
  }
}
function get(record,key){
  let pos=-1
  //遍历数组,找到对应的对象的位置
  for(let i=0;i<record.length;i++){
    if(record[i].key===key){
      pos = i
      break
    }
  }
  //如果找到,则先从当前位置删除,并将其移入头部位置,作为最常用
  if(pos!=-1){
    let res = record.splice(pos,1)
    record.unshift(res[0])
  }
  //默认pos为-1没找到
  return pos
}

  function LRU( operators ,  k ) {
      // write code here
    let res=[]
    let record=[]
    for(let i=0;i<operators.length;i++){
      //根据操作数组长度判断是设置值还是取值
      if(operators[i].length===3){
        set(record,operators[i][1],operators[i][2],k)
      } else {
        let getres = get(record,operators[i][1])
        //如果没有找到,则推入-1,找到则推入value,此时要找的对象已经在位置[0]
        getres===-1?res.push(-1):res.push(record[0].value)
      }
    }
    return res
  }
  module.exports = {
      LRU : LRU
  };


发表于 2021-07-30 13:52:44 回复(0)
function LRU( operators ,  k ) {
    // write code here
    let res = [];
    let map = new Map();
    for(let i = 0; i < operators.length; i++){
        let [op, key, value] = operators[i];
        if(op === 1) {
            if(map.size >= k) {
                let mapIter = map.keys();
                map.delete(mapIter.next().value)//按照顺序插入Map对象中每个元素的key值
                map.set(key, value);
            } else {
                if(map.has(key)) {
                    map.delete(key)                 
                }
                map.set(key, value);
            }
        } else if(op === 2) {
            if(!map.has(key)) {
                res.push(-1);
            } else {
                let val = map.get(key);
                res.push(val);
                map.delete(key);//把其变成常用的
                map.set(key, val);
            }
        }
    }
    return res;
}
module.exports = {
    LRU : LRU
};
发表于 2021-07-29 10:43:03 回复(0)
/**
 * lru design
 * @param operators int整型二维数组 the ops
 * @param k int整型 the k
 * @return int整型一维数组
 */
function LRU( operators ,  k ) {
    let ans = [];
    this.cache = new Map();
    this.capacity = k;
    this.set = function (key, value) {
        if(this.cache.has(key)){
            this.cache.delete(key);
        }
        this.cache.set(key, value);
        if (this.cache.size > this.capacity) {
            const first = this.cache.keys().next().value;
            this.cache.delete(first)
        }
    }
    this.get = function (key) {
        if(!this.cache.has(key)) return -1;
        const value = this.cache.get(key);
        this.cache.delete(key);
        this.cache.set(key, value)
        return value;
    }
    for(let operator of operators){
       if(operator[0] === 1){
           this.set(operator[1], operator[2]);
       } else {
           ans.push(this.get(operator[1]));
       }
    }
    return ans
}

module.exports = {
    LRU : LRU
};

发表于 2021-07-28 14:07:48 回复(0)
一看就懂  javascript版
/**
* lru design
* @param operators int整型二维数组 the ops
* @param k int整型 the k
* @return int整型一维数组
*/
/* 双向链表node*/
class LinkNode {
  constructor(key, value) {
    this.key = key;
    this.val = value;
    this.front = null;
    this.next = null;
  }
}
// LRU缓存数据结构
class LRUCache {
  constructor(capacity) {
    // 容量
    this.capacity = capacity;
    // cache链表元素map
    this.map = new Map();
    // 头
    this.head = new LinkNode(0, 0);
    // 尾
    this.tail = new LinkNode(0, 0);
    // 头==next==>尾
    this.head.next = this.tail;
    // 头<==front==尾
    this.tail.front = this.head;
  }
  // 增加
  set(key, value) {
    // 如果不在map里 添加进来
    if (!this.map.has(key)) {
      // 如果已经满容量了 删除尾部最后一个节点
      if (this.capacity === this.map.size) {
        this.deleteLastNode();
      }
      const temp = this.head.next;
      const node = new LinkNode(key, value);
      node.next = temp;
      node.front = this.head;
      this.head.next = node;
      temp.front = node;
      this.map.set(key, node);
    } else {
      const node = this.map.get(key);
      node.val = value;
      this.moveNodeTopTop(node);
    }

  }
  // 获取
  get(key) {
    if (this.map.has(key)) {
      const node = this.map.get(key);
      this.moveNodeTopTop(node);
      return node.val;
    }
    return -1;
  }
  // 删除最后一个节点
  deleteLastNode() {
    const lastNode = this.tail.front;
    lastNode.front.next = this.tail;
    this.tail.front = lastNode.front;
    this.map.delete(lastNode.key);
  }
  // 把节点移动到头部变成最新的
  moveNodeTopTop(node) {
    node.front.next = node.next;
    node.next.front = node.front;
    const temp = this.head.next;
    this.head.next = node;
    node.front = this.head;
    node.next = temp;
    temp.front = node;
  }
}
function LRU(operators, k) {
  const lruCache = new LRUCache(k);
  // 存储
  const res = [];
  for (let i = 0; i < operators.length; i++) {
    const item = operators[i];
    if (item.length === 3) {
      lruCache.set(item[1], item[2]);
    } else {
      // 拿最新的
      const val = lruCache.get(item[1]);
      res.push(val);
    }
  }
  return res;
}
module.exports = {
  LRU: LRU
};


发表于 2021-05-20 22:36:10 回复(0)
用双链表,本来是超时的,加了个判断就勉强过了。
/**
 * lru design
 * @param operators int整型二维数组 the ops
 * @param k int整型 the k
 * @return int整型一维数组
 */
function LRU( operators ,  k ) {
    const map = new Map();
    let head = new LinkNode(-1, -1);
    let tail = head;
    head.next = tail;
    tail.prev = head;
    function setHead(linkNode) {
        linkNode.next = head;
        linkNode.prev = tail;
        head.prev = linkNode;
        tail.next = linkNode;
        head = linkNode;
    }
    function deleteNode(node) {
        node.prev.next = node.next;
        node.next.prev = node.prev;
    }
    let result = [];
    for(let i = 0; i < operators.length; i++) {
        const [op, key, val] = operators[i];
        const currentNode = map.get(key);
        if(currentNode) {
            if(currentNode !== head) { // 加了这一句就过了
                deleteNode(currentNode);
                setHead(currentNode);
            }
            if(op===2) {
              result.push(currentNode.val);
            } else {
              currentNode.val = val;
            }
        } else {
            if(op === 2) {
              result.push(-1)
            } else {
                const newNode = new LinkNode(key ,val);
                map.set(key, newNode);
                setHead(newNode);
                if(map.size > k) {
                    map.delete(tail.prev.key);
                    deleteNode(tail.prev);
                }
            }
        }
    }
    return result;
}

function LinkNode(key, val) {
    this.key = key;
    this.val = val;
    this.prev = null;
    this.next = null;
}
module.exports = {
    LRU : LRU
};


编辑于 2021-03-01 00:58:06 回复(0)
/**
 * lru design
 * @param operators int整型二维数组 the ops
 * @param k int整型 the k
 * @return int整型一维数组
 */
    function ListNode(key, value) {
      this.key = key
      this.value = value
      this.pre = this.next = null
    }

    function LRUCache(capacity) {
      this.size = 0
      this.capacity = capacity
      this.map = new Map()
      // doublelist dummy node is used for need not check the head&nbs***bsp;tail
      this.dummyHead = new ListNode(-1,-1)
      this.dummyTail = new ListNode(-1,-1)
      this.dummyHead.next = this.dummyTail
      this.dummyTail.pre = this.dummyHead

    }
    LRUCache.prototype.get = function(key) {
      if (!this.map.has(key)) return -1
      let node = this.map.get(key)
      this._move2head(node)
      return node.value
    }
    LRUCache.prototype.put = function(key, value) {
      let node = this.map.get(key)
      if (node) {
        node.value = value
        this._move2head(node)
      } else {
        let node = new ListNode(key, value)
        this._addHead(node)
        this.map.set(key, node)
        this.size++
        if (this.size > this.capacity) {
          let node = this.dummyTail.pre
          this._removeNode(node)
          this.map.delete(node.key)
        }
      }
    }
    LRUCache.prototype._move2head = function(node) {
      this._removeNode(node)
      this._addHead(node)
    }
    LRUCache.prototype._removeNode = function(node) {
      node.pre.next = node.next
      node.next.pre = node.pre
    }
    LRUCache.prototype._addHead = function(node) {
      node.next = this.dummyHead.next
      this.dummyHead.next = node
      node.next.pre = node
      node.pre = this.dummyHead
    }
function LRU( operators ,  k ) {
    let ans = []
    let lru = new LRUCache(k)
    for (let i = 0; i < operators.length; i++) {
        if (operators[i][0] == 1) {
            lru.put(operators[i][1], operators[i][2])
        } else {
            ans.push(lru.get(operators[i][1]))
        }
    }
    return ans
    
    
    
    
}
module.exports = {
    LRU : LRU
}
自己实现双向链表超时,只用map就不超时,牛客的系统是真的比不上LeetCode
发表于 2021-02-17 14:17:55 回复(0)
function LRU( operators ,  k ) {
    // write code here
    let res = [];
    let map = new Map();
    for(let i = 0; i < operators.length; i++){
        let [op, key, value] = operators[i];
        if(op === 1) {
            if(map.size >= k) {
                let mapIter = map.keys();
                map.delete(mapIter.next().value)//按照顺序插入Map对象中每个元素的key值
                map.set(key, value);
            } else {
                if(map.has(key)) {
                    map.delete(key)                 
                }
                map.set(key, value);
            }
        } else if(op === 2) {
            if(!map.has(key)) {
                res.push(-1);
            } else {
                let val = map.get(key);
                res.push(val);
                map.delete(key);//把其变成常用的
                map.set(key, val);
            }
        }
    }
    return res;
}

发表于 2021-01-12 18:15:59 回复(0)
这样写为什么提交不成功呀
/**
 * lru design
 * @param operators int整型二维数组 the ops
 * @param k int整型 the k
 * @return int整型一维数组
 */
function LRU( operators ,  k ) {
    const map = new Map();
    const arr = [];
    const result = [];
    for (let op of operators) {
        if (op[0] === 1) {
            map.set(op[1], op[2])
        } else if (op[0] === 2) {
            result.push(map.has(op[1]) ? map.get(op[1]) : -1)
        }
        let has = false;
        let index = arr.indexOf(op[1])
        if (index !== -1) {
            arr.splice(index, 1);
        }
        arr.push(op[1])
        if (arr.length > k) {
            const num = arr.shift();
            map.delete(num);
        }
    }
    return result;
}
module.exports = {
    LRU : LRU
};

测试是能通过的,但是提交就超时,还提示通过率为0
发表于 2021-01-05 11:13:29 回复(0)
为什么一样的代码,我就超时
发表于 2020-12-14 18:37:40 回复(3)
体验是真的差, 还是力扣好啊
function LRU( operators ,  k ) {
    // write code here
  this.map = new Map()
  this.set = function (key, val) {
    if (this.map.has(key)) {
      this.map.delete(key)
    } else {
      if (this.map.size === k) {
        let keys = this.map.keys()
        let key = keys.next().value
        this.map.delete(key)
      }
    }
    this.map.set(key, val)
  }
  this.get = function (key) {
    if (this.map.has(key)){
      let val = this.map.get(key)
      this.map.delete(key)
      this.map.set(key, val)
      return val
    }
    return -1
  }
  let res = []
  for(let o of operators){
    if (o[0] === 1) {
      this.set(o[1], o[2])
    } else if (o[0] === 2) {
      res.push(this.get(o[1]))
    }
  }
  return res
}
module.exports = {
    LRU : LRU
};


编辑于 2020-12-01 17:16:13 回复(0)