This is the sixth day of my participation in the First Challenge 2022. For details: First Challenge 2022.

describe

You design and implement data structures for least-used (LFU) caching algorithms.

Implement the LFUCache class:

LFUCache(int Capacity) – Initializes the object with the capacity of the data structure

Int get(int key) – Gets the value of the key if it exists in the cache, otherwise -1 is returned.

Void put(int key, int value) – If the key already exists, change its value; If the key does not exist, insert a key-value pair.

When the cache reaches its capacity, the least frequently used items should be removed before new items are inserted.

In this problem, when there is a tie (that is, two or more keys with the same frequency of use), the most recent and oldest unused key should be removed.

To determine the least-used key, you can maintain a usage counter for each key in the cache. The key with the smallest use count is the key that has not been used for the longest time.

When a key is first inserted into the cache, its usage counter is set to 1 (due to the PUT operation). Perform a GET or put operation on a key in the cache, and the value of the use counter will be incremented.

The functions get and PUT must run in O(1) average time complexity.

Their thinking

1. Construct the node structure

Save the corresponding data information

const LFUNode = function (
  key = "",
  val = "",
  freq = 0,
  pre = null,
  next = null
) {
  this.key = key;
  this.val = val;
  this.freq = freq;
  this.pre = pre;
  this.next = next;
};
Copy the code

2. Construct bidirectional linked lists

Construct a bidirectional linked list with head and tail nodes, head and tail are sentinel nodes, do not store information, only used to mark the head and tail.

const LFULinkLine = function (node) {
  let head = new LFUNode("head");
  let tail = new LFUNode("tail");
  head.next = node;
  tail.pre = node;
  node.pre = head;
  node.next = tail;
  this.head = head;
  this.tail = tail;
};
Copy the code

3, write a list header to add a node method

After inserting a node into the head of the list, the other nodes move back.

LFULinkLine.prototype.addNode = function (node) {
  this.head.next.pre = node;
  node.pre = this.head;
  node.next = this.head.next;
  this.head.next = node;
};
Copy the code

4. Write methods to delete nodes

Remove the node from the linked list.

LFULinkLine.prototype.removeNode = function (node) {
  node.pre.next = node.next;
  node.next.pre = node.pre;
};
Copy the code

5. Construct LRU cache structure

Capacity is used to store the maximum number of caches, that is, the capacity of the class. Num Stores the amount of data currently stored, which is used to determine whether the maximum capacity is exceeded. MinFreq stores the minimum frequency of the data currently stored. When deleting data, the lowest frequency should be deleted first. KvMap stores detailed node information. The index is the key value of the node. FreqMap stores the linked list information of the corresponding frequency. The index is the FREQ (frequency) of the node. When deleting the node, you can quickly obtain the information about the node to be deleted from this list.

/ * * *@param {number} capacity* /
var LFUCache = function (capacity) {
  this.capacity = capacity;// Maximum cache
  this.num = 0;// The current number
  this.minFreq = 0;// Current minimum frequency
  this.kvMap = new Map(a);// Save the node information
  this.freqMap = new Map(a);// Save the linked list information of the corresponding frequency
};
Copy the code

6. Write the GET method

There are two main cases of GET:

(1) The node exists

  • The corresponding node is obtained through kvMap
  • Delete the node from freqMap
  • Check whether minFreq needs to be modified
  • Example Change the freQ of a controller
  • Re-insert the node into freqMap
  • Returns the value of the node

(2) The node does not exist

If capacity is 0 or the node information does not exist in kvMap, return -1.

/ * * *@param {number} key
 * @return {number}* /
LFUCache.prototype.get = function (key) {
  if (this.capacity === 0) return -1;
  if (!this.kvMap.has(key)) return -1;
  // Obtain the corresponding node through kvMap
  let node = this.kvMap.get(key);
  let linkLine = this.freqMap.get(node.freq);
  // Delete the node from freqMap
  linkLine.removeNode(node);
  // Determine whether minFreq needs to be modified
  / / to empty
  if (linkLine.head.next === linkLine.tail) {
    this.freqMap.delete(node.freq);
    if (this.minFreq == node.freq) this.minFreq++;
  }
  // Change the freq of the controller
  node.freq++;
  // Re-insert the node into freqMap
  if (this.freqMap.has(node.freq)) {
    linkLine = this.freqMap.get(node.freq);
    linkLine.addNode(node);
  } else {
    this.freqMap.set(node.freq, new LFULinkLine(node));
  }
  // Returns the value of the node
  return node.val;
};
Copy the code

7. Write the put method

The PUT operation has the following two scenarios:

(1) Update

  • The corresponding node is obtained through kvMap
  • Delete the node from freqMap
  • Check whether minFreq needs to be modified
  • Updating Node Information
  • Re-insert the node into freqMap

(2) Deposit

In the case of deposit, there are two cases:

Capacity is full
  • Use minFreq to find the node to delete
  • Delete the node from freqMap
  • Check whether minFreq needs to be modified
  • Insert the new node into freqMap
  • Update minFreq
Capacity under
  • Modify the num
  • Insert the new node into freqMap
  • Update minFreq
/ * * *@param {number} key
 * @param {number} value
 * @return {void}* /
LFUCache.prototype.put = function (key, value) {
  if (this.capacity === 0) return;
  if (this.kvMap.has(key)) {
    / / update
    // Obtain the corresponding node through kvMap
    let node = this.kvMap.get(key);
    // Delete the node from freqMap
    let linkLine = this.freqMap.get(node.freq);
    linkLine.removeNode(node);
    // Determine whether minFreq needs to be modified
    if (linkLine.head.next === linkLine.tail) {
      if (this.minFreq == node.freq) this.minFreq++;
      this.freqMap.delete(node.freq);
    }
    // Update node information
    node.val = value;
    node.freq++;
    // Re-insert the node into freqMap
    if (this.freqMap.has(node.freq)) {
      linkLine = this.freqMap.get(node.freq);
      linkLine.addNode(node);
    } else {
      linkLine = new LFULinkLine(node);
      this.freqMap.set(node.freq, linkLine); }}else {/ / in
    if (this.capacity == this.num) {/ / is full
      // Use minFreq to find the node to delete
      let freq = this.minFreq;
      let linkLine = this.freqMap.get(freq);
      let node = linkLine.tail.pre;
      // Delete the node from freqMap
      linkLine.removeNode(node);
      this.kvMap.delete(node.key);
      // Determine whether minFreq needs to be modified
      if (linkLine.head.next === linkLine.tail) {
        this.freqMap.delete(node.freq); }}else {
      / / modify num
      this.num++;
    }
    // Insert the new node into freqMap
    let node = new LFUNode(key, value, 0);
    this.kvMap.set(key, node);
    if (this.freqMap.has(0)) {
      let linkLine = this.freqMap.get(0);
      linkLine.addNode(node);
    } else {
      let linkLine = new LFULinkLine(node);
      this.freqMap.set(0, linkLine);
    }
    / / update the minFreq
    this.minFreq = 0; }};Copy the code

code

const LFUNode = function (
  key = "",
  val = "",
  freq = 0,
  pre = null,
  next = null
) {
  this.key = key;
  this.val = val;
  this.freq = freq;
  this.pre = pre;
  this.next = next;
};
const LFULinkLine = function (node) {
  let head = new LFUNode("head");
  let tail = new LFUNode("tail");
  head.next = node;
  tail.pre = node;
  node.pre = head;
  node.next = tail;
  this.head = head;
  this.tail = tail;
};
LFULinkLine.prototype.addNode = function (node) {
  this.head.next.pre = node;
  node.pre = this.head;
  node.next = this.head.next;
  this.head.next = node;
};
LFULinkLine.prototype.removeNode = function (node) {
  node.pre.next = node.next;
  node.next.pre = node.pre;
};

/ * * *@param {number} capacity* /
var LFUCache = function (capacity) {
  this.capacity = capacity;
  this.num = 0;
  this.minFreq = 0;
  this.kvMap = new Map(a);this.freqMap = new Map(a); };/ * * *@param {number} key
 * @return {number}* /
LFUCache.prototype.get = function (key) {
  if (this.capacity === 0) return -1;
  if (!this.kvMap.has(key)) return -1;
  let node = this.kvMap.get(key);
  let linkLine = this.freqMap.get(node.freq);
  linkLine.removeNode(node);
  / / to empty
  if (linkLine.head.next === linkLine.tail) {
    this.freqMap.delete(node.freq);
    if (this.minFreq == node.freq) this.minFreq++;
  }
  node.freq++;
  if (this.freqMap.has(node.freq)) {
    linkLine = this.freqMap.get(node.freq);
    linkLine.addNode(node);
  } else {
    this.freqMap.set(node.freq, new LFULinkLine(node));
  }
  return node.val;
};

/ * * *@param {number} key
 * @param {number} value
 * @return {void}* /
LFUCache.prototype.put = function (key, value) {
  if (this.capacity === 0) return;
  if (this.kvMap.has(key)) {
    / / update
    let node = this.kvMap.get(key);
    node.val = value;
    let linkLine = this.freqMap.get(node.freq);
    linkLine.removeNode(node);
    if (linkLine.head.next === linkLine.tail) {
      if (this.minFreq == node.freq) this.minFreq++;
      this.freqMap.delete(node.freq);
    }
    node.freq++;
    if (this.freqMap.has(node.freq)) {
      linkLine = this.freqMap.get(node.freq);
      linkLine.addNode(node);
    } else {
      linkLine = new LFULinkLine(node);
      this.freqMap.set(node.freq, linkLine); }}else {
    / / in
    if (this.capacity == this.num) {
      / / is full
      let freq = this.minFreq;
      let linkLine = this.freqMap.get(freq);
      let node = linkLine.tail.pre;
      linkLine.removeNode(node);
      this.kvMap.delete(node.key);

      if (linkLine.head.next === linkLine.tail) {
        this.freqMap.delete(node.freq); }}else {
      this.num++;
    }
    let node = new LFUNode(key, value, 0);
    this.kvMap.set(key, node);
    if (this.freqMap.has(0)) {
      let linkLine = this.freqMap.get(0);
      linkLine.addNode(node);
    } else {
      let linkLine = new LFULinkLine(node);
      this.freqMap.set(0, linkLine);
    }
    this.minFreq = 0; }};/** * Your LFUCache object will be instantiated and called as such: * var obj = new LFUCache(capacity) * var param_1 = obj.get(key) * obj.put(key,value) */
Copy the code