数据结构

LRU缓存

146.LRU缓存机制

我是链接

LRU 的全称是Least Recently Used最近最少使用缓存淘汰策略。

  1. // 实现双向链表节点类
  2. class ListNode {
  3. constructor (key, value) {
  4. this.key = key
  5. this.value = value
  6. this.prev = null
  7. this.next = null
  8. }
  9. }
  10. class LRUCache {
  11. constructor (capacity) {
  12. this.capacity = capacity // 容量
  13. this.count = 0 // 已存储个数
  14. this.hash = {} // 记录 key => 节点 的映射
  15. this.dummyHead = new ListNode()
  16. this.dummyTail = new ListNode()
  17. this.dummyHead.next = this.dummyTail
  18. this.dummyTail.prev = this.dummyHead
  19. }
  20. // key 存在,将节点移到链表头部(删除当前节点 + 将节点添加到头部),否则,返回 -1
  21. get (key) {
  22. const node = this.hash[key]
  23. if (node) {
  24. this.removeFromList(node)
  25. this.addToHead(node)
  26. return node.value
  27. }
  28. return -1
  29. }
  30. // key存在,将value值替换,将节点移动到最前面。
  31. // key不存在:1.容量已满,删除尾节点,节点数减一,将新节点加入头部; 2.容量未满,将新节点加入头部,节点数加一
  32. put (key, value) {
  33. let node = this.hash[key]
  34. if (node) {
  35. node.value = value
  36. this.removeFromList(node)
  37. this.addToHead(node)
  38. } else {
  39. node = new ListNode(key, value)
  40. if (this.count === this.capacity) {
  41. const tail = this.dummyTail.prev
  42. delete this.hash[tail.key]
  43. this.count --
  44. this.removeFromList(tail)
  45. }
  46. this.addToHead(node)
  47. this.hash[key] = node
  48. this.count ++
  49. }
  50. }
  51. removeFromList (node) {
  52. node.prev.next = node.next
  53. node.next.prev = node.prev
  54. }
  55. // 添加节点至头部
  56. addToHead (node) {
  57. node.prev = this.dummyHead
  58. node.next = this.dummyHead.next
  59. this.dummyHead.next.prev = node
  60. this.dummyHead.next = node
  61. }
  62. }

460. LFU 缓存

我是链接

维护 KV 表,KF 表,FK 表三个映射。

  1. class LFUCache {
  2. constructor (capacity) {
  3. this.capacity = capacity
  4. this.KVMap = new Map() // key => value
  5. this.KFMap = new Map() // key => freq
  6. this.FKMap = new Map() // freq => Set(key1, key2),这里用 Set 存储 key 可以方便增删去重
  7. this.minFreq = 0
  8. }
  9. addFreq (key) {
  10. // 获取现有次数 和 次数相应的 key 集合
  11. const freq = this.KFMap.get(key)
  12. const keySet = this.FKMap.get(freq)
  13. // 更新最小 freq
  14. if (this.minFreq === freq && keySet.size === 1) {
  15. this.minFreq += 1
  16. }
  17. // 当前次数集合去除原始key
  18. keySet.delete(key)
  19. // 更新 KF 和 FKSet
  20. this.KFMap.set(key, freq + 1)
  21. let newKeySet = this.FKMap.get(freq + 1)
  22. if (!newKeySet) {
  23. newKeySet = new Set()
  24. this.FKMap.set(freq + 1, newKeySet)
  25. }
  26. newKeySet.add(key)
  27. }
  28. get (key) {
  29. if (this.KVMap.has(key)) {
  30. // 增加次数
  31. this.addFreq(key)
  32. return this.KVMap.get(key)
  33. }
  34. return -1
  35. }
  36. put (key, value) {
  37. if (this.capacity === 0) return
  38. if (this.KVMap.has(key)) {
  39. this.KVMap.set(key, value)
  40. this.addFreq(key)
  41. } else {
  42. // 容量已满
  43. if (this.KVMap.size === this.capacity) {
  44. // 找到最小次数对应的最老 key
  45. const minKeySet = this.FKMap.get(this.minFreq) // Set{key1, key2}, 取出 key1
  46. const minKey = minKeySet.keys().next().value
  47. // 删除映射中的 key
  48. this.KVMap.delete(minKey)
  49. this.KFMap.delete(minKey)
  50. minKeySet.delete(minKey)
  51. }
  52. // 新增一个 key 及其映射
  53. this.KVMap.set(key, value)
  54. this.KFMap.set(key, 1)
  55. let oneSet = this.FKMap.get(1)
  56. if (!oneSet) {
  57. oneSet = new Set()
  58. this.FKMap.set(1, oneSet)
  59. }
  60. oneSet.add(key)
  61. this.minFreq = 1
  62. }
  63. }
  64. }

单调栈

// 模板
let res = [] // 存放答案
let stack = []
for (let i = n - 1; i >= 0; i --) {
  let top = stack[stack.length - 1]
  while (stack.length && top <= nums[i]) {
    stack.pop()
  }
  let restTop = stack[stack.length - 1]
  res[i] = stack.length ? restTop : -1
  stack.push(nums[i])
}

739.每日温度

我是链接

var dailyTemperatures = function(temperatures) {
  let res = []
  let deque = []
  for (let i = temperatures.length - 1; i >= 0; i --) {
    while (deque.length && temperatures[deque[deque.length - 1]] <= temperatures[i]) {
      deque.pop()
    }
    res[i] = deque.length ? deque[deque.length - 1] - i : 0
    deque.push(i)
  }
  return res
};

496.下一个更大的元素I

我是链接

var nextGreaterElement = function(nums1, nums2) {
  const n = nums2.length
  let s = []
  let hash = {} //存放答案,作为 nums1 的映射
  let res = []
  for (let i = n - 1; i >= 0; i --) {
    while (s.length && s[s.length - 1] <= nums2[i]) {
      s.pop()
    }
    hash[nums2[i]] = s.length ? s[s.length - 1] : -1
    s.push(nums2[i])
  }
  for (let i = 0; i < nums1.length; i ++) {
    res[i] = hash[nums1[i]] || -1
  }
  return res
};

503.下一个更大的元素II

我是链接

// 这里数组是环形的,可以将原数组接在最后一个元素后面
var nextGreaterElements = function(nums) {
  let arr = [...nums, ...nums]
  const n = arr.length
  let res = []
  let s = []
  for (let i = n - 1; i >= 0; i --) {
    while (s.length && s[s.length - 1] <= arr[i]) {
      s.pop()
    }
    res[i] = s.length ? s[s.length - 1] : -1
    s.push(arr[i])
  }
  return res.slice(0, n / 2)
};

处理环形数组 🌟🌟

对索引进行求模,模拟长度加长的情况

// let arr = [1, 2, 3]
// const n = arr.length
// for (let i = 2 * n - 1; i >= 0; i --) {
//     console.log(i%n, arr[i%n])
// }
var nextGreaterElements = function(nums) {
  const n = nums.length
  let res = []
  let s = []
  for (let i = 2 * n - 1; i >= 0; i --) {
    while (s.length && s[s.length - 1] <= nums[i % n]) {
      s.pop()
    }
    res[i % n] = s.length ? s[s.length - 1] : -1
    s.push(nums[i % n])
  }
  return res
};

单调队列

239.滑动窗口最大的值

我是链接

var maxSlidingWindow = function(nums, k) {
  const n = nums.length
  let dequeue = []
  let res = []
  for (let i = 0; i < n; i ++) {
    // 合法性检测,超出 k个 窗口删除
    if (i - dequeue[0] >= k) {
      dequeue.shift()
    }
    while (dequeue.length && nums[dequeue[dequeue.length - 1]] <= nums[i]) {
      dequeue.pop()
    }
    dequeue.push(i)
    // k 为 3, 区间[0-2]开始算最大值
    if (i >= k -1) {
      res.push(nums[dequeue[0]])
    }
  }
  return res
};

最小栈

155. 最小栈

我是链接

// 思路: 用两个栈,一个min栈用来存每次新增时当前栈最小值
class MinStack {
  constructor () {
    this.stack = []
    this.minStack = []
  }
  top () {
    return this.stack[this.stack.length - 1]
  }
  push (x) {
    this.stack.push(x)
    const min = Math.min(...this.stack)
    this.minStack.push(min)
  }
  pop () {
    this.stack.pop()
    this.minStack.pop()
  }
  getMin () {
    return this.minStack[this.minStack.length - 1]
  }
}