LRU缓存
标准双向链表+hashMap解法:
关注点:
- 使用双向列表=>做到删除新增都是O(1)
- 双向链表的结点=>存 key 和 对应的数据值。
- 哈希表的存在意义:快速访问双向链表的节点,而不是用来存储数据
- key:双向链表中存的 key
- value:链表结点的引用。
- 虚拟头尾的意义=>统一所有节点的操作而不需要判断头尾节点的特殊性
class ListNode {
constructor(key, value) {
this.key = key
this.value = value
this.next = null
this.prev = null
}
}
class LRUCache {
constructor(capacity) {
this.capacity = capacity
this.hash = {}
this.count = 0
this.dummyHead = new ListNode()
this.dummyTail = new ListNode()
this.dummyHead.next = this.dummyTail
this.dummyTail.prev = this.dummyHead
}
get(key) {
let node = this.hash[key]
if (node == null) return -1
this.moveToHead(node)
return node.value
}
put(key, value) {
let node = this.hash[key]
if (node == null) {
if (this.count == this.capacity) {
this.removeLRUItem()
}
let newNode = new ListNode(key, value)
this.hash[key] = newNode
this.addToHead(newNode)
this.count++
} else {
node.value = value
this.moveToHead(node)
}
}
moveToHead(node) {
this.removeFromList(node)
this.addToHead(node)
}
removeFromList(node) {
let temp1 = node.prev
let temp2 = node.next
temp1.next = temp2
temp2.prev = temp1
}
addToHead(node) {
node.prev = this.dummyHead
node.next = this.dummyHead.next
this.dummyHead.next.prev = node
this.dummyHead.next = node
}
removeLRUItem() {
let tail = this.popTail()
delete this.hash[tail.key]
this.count--
}
popTail() {
let tail = this.dummyTail.prev
this.removeFromList(tail)
return tail
}
}
/**
* Your LRUCache object will be instantiated and called as such:
* var obj = new LRUCache(capacity)
* var param_1 = obj.get(key)
* obj.put(key,value)
*/
利用ES6中Map有序的解法:
关注点:
- Map中最先set的是在第一位
- this.map.keys()返回该map中所有key值的可迭代对象
- next()方法调用迭代器,返回格式形如:
- {value:...,done:...}
- 因此最后的.value拿到的其实是map中第一位的key值~
/**
* @param {number} capacity
*/
var LRUCache = function (capacity) {
this.capacity = capacity
this.map = new Map()
};
/**
* @param {number} key
* @return {number}
*/
LRUCache.prototype.get = function (key) {
if (this.map.has(key)) {
const value = this.map.get(key)
this.map.delete(key)
this.map.set(key, value)
return value
} else return -1
};
/**
* @param {number} key
* @param {number} value
* @return {void}
*/
LRUCache.prototype.put = function (key, value) {
if (this.map.has(key)) {
this.map.delete(key)
this.map.set(key, value)
} else {
this.map.size === this.capacity
?
this.map.delete(this.map.keys().next().value)
:null
this.map.set(key, value)
}
};
/**
* Your LRUCache object will be instantiated and called as such:
* var obj = new LRUCache(capacity)
* var param_1 = obj.get(key)
* obj.put(key,value)
*/
关于js中迭代器和生成器可以看我这篇文章的介绍:juejin.cn/post/736944…
括号生成
常规回溯解法:
关注点:
- 回溯的核心在于每次递归前进行相应操作,并在递归后进行恢复
- 在括号生成中可以利用左括号的数量一定大于等于右括号的数量进行剪枝
var generateParenthesis = function (n) {
let resArr = [];
let res = ''; //当前的括号串
let left = 0, //当前的左括号数
right = 0 //当前的右括号数
function backtrack() {
if (left >= n) {//如果左边括号数够了,直接补齐右括号然后存入答案
let k = left - right
while (k > 0) {
k--
res = res + ')'
}
resArr.push(res)
//记得恢复原样
k = left - right
res = res.slice(0, -k)
} else {
//两种情况,增加左括号或者增加右括号
for (let i = 0; i < 2; i++) {
if (i == 0) {
left++
res = res + '('
backtrack()
left--
res = res.slice(0, -1)
} else if (right < left) {
//左右逻辑相同,但是左边括号数量一定大于右边时候才能添加新的右边括号
right++
res = res + ')'
backtrack()
right--
res = res.slice(0, -1)
}
}
}
}
backtrack()
return resArr
}
暴力递归+set去重:
如果使用记事本对每个n的答案括号串记录起来可以空间换时间提高一点速度
var generateParenthesis = (initn) => {
const func = (n) => {
if (n == 1) {
return ['()']
}
let res = func(n - 1)
let pjRes = []
for (let i = 0; i < res.length; i++) {
let str = res[i]
let sL = str.length
for (let j = 0; j < sL; j++) {
pjRes.push(`${str.slice(0, j)}()${str.slice(j, sL)}`)
}
}
return pjRes
}
return Array.from(new Set(func(initn)))
}