lruCache

Written on September 10, 2019

문제

Design and implement an LRU, or Least Recently Used, cache.

An LRU cache gives O(1) get(key) and set(key, val) operations, much like a hashtable, but once it reaches its limit for stored number of items, removes the least recently used (i.e. the oldest by get-date) item from the cache in O(1) time.

For instance:

var cache = new LRUCache(3); // limit of 3 items
cache.set("item1", 1);
cache.set("item2", 2);
cache.set("item3", 3);
cache.set("item4", 4);

cache.get("item3"); //=> 3
cache.get("item2"); //=> 2
// item1 was removed because it was the oldest item by insertion/usage
cache.get("item1"); //=> null

// item4 is removed to make room, because it is the oldest by usage,
// which takes priority.
cache.set("item5", 5);

// item3 is also removed, because it was retrieved before item2 was
// last retrieved.
cache.set("item6", 6);

You will need a doubly-linked list (provided).

풀이

var LRUCache = function(limit) {
  this.list = new List();
  this.count = 0;
  this.limit = limit;
};

var LRUCacheItem = function(val, key) {};

LRUCache.prototype.size = function() {
  return this.count;
};

LRUCache.prototype.get = function(key) {
  let returnVal = null;
  let node = this.list.head;
  while (node.next) {
    if (node.val[key] !== undefined) {
      returnVal = node.val[key];
      this.list.moveToEnd(node);
      break;
    } else {
      node = node.next;
    }
  }
  return returnVal;
};

LRUCache.prototype.set = function(key, val) {
  if (this.count === this.limit) {
    this.list.shift();
    this.count--;
  }
  let item = {};
  item[key] = val;
  this.list.push(item);
  this.count++;
};

var List = function() {
  this.head = null;
  this.tail = null;
};

var ListNode = function(prev, val, next) {
  this.prev = prev || null;
  this.val = val;
  this.next = next || null;
};

// Insert at the head of the list.
List.prototype.unshift = function(val) {
  // Empty list
  if (this.head === null && this.tail === null) {
    this.head = this.tail = new ListNode(null, val, null);
    // Not empty list.
  } else {
    this.head = new ListNode(null, val, this.head);
    this.head.next.prev = this.head;
  }

  return this.head;
};

// Delete at the head of the list.
List.prototype.shift = function() {
  // Empty list
  if (this.head === null && this.tail === null) {
    return null;
    // Not empty list.
  } else {
    var head = this.head;
    this.head = this.head.next;
    head.delete();
    return head.val;
  }
};

// Insert at the end of the list.
List.prototype.push = function(val) {
  // Empty list
  if (this.head === null && this.tail === null) {
    this.head = this.tail = new ListNode(null, val, null);
    // Not empty list.
  } else {
    this.tail = new ListNode(this.tail, val, null);
    this.tail.prev.next = this.tail;
  }

  return this.tail;
};

// Delete at the end of the list.
List.prototype.pop = function() {
  // Empty list
  if (this.head === null && this.tail === null) {
    return null;
    // Not empty list.
  } else {
    var tail = this.tail;
    this.tail = this.tail.prev;
    tail.delete();
    return tail.val;
  }
};

// Move a node to the front of the List
List.prototype.moveToFront = function(node) {
  if (node === this.tail) {
    this.pop();
  } else if (node === this.head) {
    return;
  } else {
    node.delete();
  }

  node.prev = node.next = null;

  // Don't delegate to shift, since we want to keep the same
  // object.

  // Empty list
  if (this.head === null && this.tail === null) {
    this.head = this.tail = node;
    // At least one node.
  } else {
    this.head.prev = node;
    node.next = this.head;
    this.head = node;
  }
};

// Move a node to the end of the List
List.prototype.moveToEnd = function(node) {
  if (node === this.head) {
    this.shift();
  } else if (node === this.tail) {
    return;
  } else {
    node.delete();
  }

  // Don't delegate to push, since we want to keep the same
  // object.

  node.prev = node.next = null;

  // Empty list
  if (this.head === null && this.tail === null) {
    this.head = this.tail = node;
    // At least one node.
  } else {
    this.tail.next = node;
    node.prev = this.tail;
    this.tail = node;
  }
};

ListNode.prototype.delete = function() {
  if (this.prev) {
    this.prev.next = this.next;
  }
  if (this.next) {
    this.next.prev = this.prev;
  }
};

👩🏻‍💻 배우는 것을 즐기는 프론트엔드 개발자 입니다
부족한 블로그에 방문해 주셔서 감사합니다 🙇🏻‍♀️

in the process of becoming the best version of myself