该部分属于没刷过就没思路的题型,因此需要掌握。整体难度一般。
另外,对于 LRUCache 或 栈实现队列 的题目,要把函数形式和参数都背下来,不能只会方法。
20. 有效的括号:先return false
如果是有效括号,那么 「后遇到的左括号要先闭合」,符合栈的进出结构,所以对左括号进行入栈操作;当遇到一个右括号的时候,我们会取出栈顶的左括号与之匹配,如果不是相同类型,返回false。
const isValid = function(s) {
const stack = [];
const map = new Map([ // 哈希表
[')', '('],
[']', '['],
['}', '{']
]);
for (let ch of s) {
if (map.has(ch)) { // 遇到右括号时
// 栈为空 或 栈顶元素不等于对应的左括号时
if (!stack.length || stack[stack.length - 1] != map.get(ch)) {
return false;
}
stack.pop();
} else { // 遇到左括号就直接入栈
stack.push(ch);
}
}
return stack.length == 0; // 注意!!当s全为左括号时
}
优化:开头增加对s.length % 2 == 1的判断,如果是奇数则一定不是有效括号,因为有效括号长度只能为偶数。
时间复杂度:O(n)。
空间复杂度:O(n + len)。 其中n是栈的长度,len是哈希表的长度,这里等于为6。
146. LRU 缓存:Map插入顺序 + keys().next()
LRU (Least Recently Used,最近最少使用) 缓存淘汰算法:根据数据的历史访问记录来淘汰数据,核心思想是 “如果数据最近被访问过,那么将来被访问的几率也更高” 。因此当某个key的put或get操作一旦发生,就认为这个key的记录成了最常使用的,然后就会刷新缓存。
-
Map结构是有序的,相比于传统哈希表,Map实际上是哈希链表,它能够记住键的原始插入顺序。 因此我们可以把key-value按照最后的使用时间排序。 -
Map.prototype.keys()返回一个iterator,一旦 size 超过限度,iterator.next()可以按照顺序获取到最早插入的key,再通过delete删除该key即可。
const map = new Map([['a', 'ab'], ['b', 'bc'], ['c', 'cd']]);
map.keys().next(); // {value: 'a', done: false}
map.keys().next().value; // 'a'
- 不论
get还是put,实际上都是修改在Map中的插入顺序。
const LRUCache = function(capacity) {
this.count = capacity;
this.map = new Map();
};
LRUCache.prototype.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;
} else {
return -1;
}
};
LRUCache.prototype.put = function(key, value) {
if (this.map.has(key)) {
this.map.delete(key);
}
this.map.set(key, value);
if (this.map.size > this.count) {
this.map.delete(this.map.keys().next().value); // next()后返回 {value: 键名, done: false }
}
};
232. 用栈实现队列
注意:if (!this.inStack) 和 if (!this.stack.length) 结果是相反的。因为 []、{} 逻辑判断时认为是 true。
const MyQueue = function() {
this.inStack = [];
this.outStack = [];
};
MyQueue.prototype.push = function(x) {
this.inStack.push(x);
};
MyQueue.prototype.pop = function() {
if (!this.outStack.length) {
this.in2Out();
}
return this.outStack.pop(); // 栈顶元素
};
MyQueue.prototype.peek = function() {
if (!this.outStack.length) {
this.in2Out();
}
return this.outStack[this.outStack.length - 1];
};
MyQueue.prototype.empty = function() { // 判断是否为空
return this.inStack.length == 0 && this.outStack.length == 0;
};
MyQueue.prototype.in2Out = function() { // 将 inStack 所有元素都 push 到 outStack
while (this.inStack.length) {
this.outStack.push(this.inStack.pop());
}
}
剑指09. 用两个栈实现一个队列
const CQueue = function() {
this.inStack = [];
this.outStack = [];
};
CQueue.prototype.appendTail = function(x) {
this.inStack.push(x);
};
CQueue.prototype.deleteHead = function() {
if (!this.outStack.length) {
this.in2out();
}
// 多一步判断
return this.outStack.length ? this.outStack.pop() : -1;
};
CQueue.prototype.in2out = function() {
while (this.inStack.length) {
this.outStack.push(this.inStack.pop());
}
}
时间复杂度: 官方解答是插入和删除操作均为O(1)。个人觉得删除操作O(n)。???
空间复杂度: O(n)。需要使用两个栈存储已有的元素。
1047. 删除字符串中的所有相邻重复项
const removeDuplicates = function(s) {
const stack = [];
for (let ch of s) {
if (stack[stack.length - 1] == ch)
stack.pop();
else
stack.push(ch);
}
return stack.join('');
}
时间复杂度:O(n)。
空间复杂度:O(n)。