这是我参与2022首次更文挑战的第8天,活动详情查看:2022首次更文挑战 | 创作学习持续成长,夺宝闯关赢大奖 - 掘金 (juejin.cn)
题目链接
- 化栈为队 leetcode-cn.com/problems/im…
- 棒球比赛 leetcode-cn.com/problems/ba…
- 比较含退格的字符串 leetcode-cn.com/problems/ba…
- 验证栈序列 leetcode-cn.com/problems/va…
- 有效的括号 leetcode-cn.com/problems/va…
题解及分析
化栈为队
实现一个MyQueue类,该类用两个栈来实现一个队列。 示例:
MyQueue queue = new MyQueue();
queue.push(1);
queue.push(2);
queue.peek(); // 返回 1
queue.pop(); // 返回 1
queue.empty(); // 返回 false
说明:
你只能使用标准的栈操作 -- 也就是只有push to top,peek/pop from top,size和is empty操作是合法的。
你所使用的语言也许不支持栈。你可以使用list或者deque(双端队列)来模拟一个栈,只要是标准的栈操作即可。
假设所有操作都是有效的(例如,一个空的队列不会调用pop或者peek操作)。
题目需要模拟实现一个队列,限制是只能使用标准的栈操作
- 维护两个栈,一个负责入队,一个负责出队
- 入队没有其他操作
- 出队需要将入队栈翻转,去掉最后一个元素(也就是入队栈的第一个元素),然后将出队栈翻转回去存回入队栈
- peek原理如出队栈
- empty判断两个栈长度是否均为空
var MyQueue = function() {
this.stackIn = []
this.stackOut = []
};
MyQueue.prototype.push = function(x) {
this.stackIn.push(x)
};
MyQueue.prototype.pop = function() {
while(this.stackIn.length > 1){
this.stackOut.push(this.stackIn.pop())
}
let ans = this.stackIn.pop()
while(this.stackOut.length){
this.stackIn.push(this.stackOut.pop())
}
return ans
};
MyQueue.prototype.peek = function() {
while(this.stackIn.length) {
this.stackOut.push(this.stackIn.pop())
}
let ans = this.stackOut[this.stackOut.length - 1]
while(this.stackOut.length) {
this.stackIn.push(this.stackOut.pop())
}
return ans
};
MyQueue.prototype.empty = function() {
return !this.stackIn.length && !this.stackOut.length;
};
棒球比赛
你现在是一场采用特殊赛制棒球比赛的记录员。这场比赛由若干回合组成,过去几回合的得分可能会影响以后几回合的得分。
比赛开始时,记录是空白的。你会得到一个记录操作的字符串列表ops,其中ops[i]是你需要记录的第i项操作,ops遵循下述规则:
整数 x - 表示本回合新获得分数x
"+" - 表示本回合新获得的得分是前两次得分的总和。题目数据保证记录此操作时前面总是存在两个有效的分数。
"D" - 表示本回合新获得的得分是前一次得分的两倍。题目数据保证记录此操作时前面总是存在一个有效的分数。
"C" - 表示前一次得分无效,将其从记录中移除。题目数据保证记录此操作时前面总是存在一个有效的分数。
请你返回记录中所有得分的总和。
思路:
- 维护一个栈用来记录数字
- 遇到不同的符号时,对栈顶元素进行操作
- 使用一个变量来记录每轮循环之后栈顶元素的和,在C的时候跳过
var calPoints = function (ops) {
let scoreStack = []
let sum = 0
for (let i = 0; i < ops.length; i++) {
if (+ops[i]){
scoreStack.push(+ops[i]
} else if (ops[i] === '+') {
scoreStack.push(scoreStack[scoreStack.length - 1] + scoreStack[scoreStack.length - 2])
} else if (ops[i] === 'C') {
sum -= scoreStack.pop()
continue;
} else if (ops[i] === 'D') {
scoreStack.push(scoreStack[scoreStack.length - 1] * 2)
}
sum += scoreStack[scoreStack.length - 1]
}
return sum
};
使用reduce:
var calPoints = function (ops) {
let scoreStack = [];
for (let i = 0; i < ops.length; i++) {
if (+ops[i]){
scoreStack.push(+ops[i]);
} else if (ops[i] === '+') {
scoreStack.push(scoreStack[scoreStack.length - 1] + scoreStack[scoreStack.length - 2])
} else if (ops[i] === 'C') {
scoreStack.pop();
} else if (ops[i] === 'D') {
scoreStack.push(scoreStack[scoreStack.length - 1] * 2)
}
}
return scoreStack.reduce((a, b) => a + b)
};
比较含退格的字符串
给定 s 和 t 两个字符串,当它们分别被输入到空白的文本编辑器后,如果两者相等,返回 true 。# 代表退格字符。
注意:如果对空文本输入退格字符,文本继续为空。
提示:
1 <= s.length, t.length <= 200
s和t只含有小写字母以及字符'#'
进阶:
你可以用O(n)的时间复杂度和O(1)的空间复杂度解决该问题吗?
方案一:栈
var backspaceCompare = function(s, t) {
return processed(s) === processed(t)
};
var processed = (str) => {
let stack = []
for(ch of str) {
if(ch === '#') {
stack.pop()
} else {
stack.push(ch)
}
}
return stack.join('')
}
方法二:
- 用两个变量记录两个字符中的#
- 同时遍历两个字符串
- 如果是#则变量加一
- 如果不是#,记录当前字符串中#的数量的变量-1,如果变量为0则跳过这次循环
- 一直到取得下一个有效字符
- 对比这两个中下标所在的字符是否相同,不同则直接返回false
var backspaceCompare = function(s, t) {
let skipS = 0
let skipT = 0
let i = s.length - 1
let j = t.length - 1;
while(i >= 0 || j >= 0) {
while(i >= 0){
if(s[i] === '#'){
skipS++
i--
}else if(skipS > 0){
skipS--
i--
}else break
}
while(j >= 0) {
if(t[j] === '#') {
skipT++
j--
} else if(skipT > 0) {
skipT--
j--
} else {
break
}
}
if(s[i] !== t[j]) return false
i--
j--
}
return true
}
验证栈序列
给定pushed和popped两个序列,每个序列中的值都不重复,只有当它们可能是在最初空栈上进行的推入push和弹出pop操作序列的结果时,返回true;否则,返回false。
这道题实际上是考察栈的出栈顺序
- 维护一个栈存入pushed
- 需要维护一个指针,这个指针每次只走一个单位
- 在出栈前对某个需要出栈的元素和popped中指针指向的元素进行比对
var validateStackSequences = function(pushed, popped) {
const stack = []
let index = 0
const length = pushed.length
for(let i = 0; i < length; i++) {
stack.push(pushed[i])
// 只对相同的字符串进行出栈,index正常情况下应该刚好和i相等
while(popped[index] !== undefined && popped[index] === stack[stack.length - 1]) {
stack.pop()
index++
}
}
return !stack.length
}
有效的括号
给定一个只包括 '(',')','{','}','[',']' 的字符串 s ,判断字符串是否有效。
有效字符串需满足:
左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
思路:
- 维护一个栈
- 遍历字符串,存入(,[,{
- 如果拿到),],},则比对栈中是否存在对应的(,[,{
var isValid = function(s) {
const n = s.length
if(n % 2 === 1) return false
// 感谢leetcode,map是真的用的少
pairs = new Map([
[')', '('],
[']', '['],
['}', '{']
])
const stack = []
for(let ch of s) {
if(pairs.has(ch)) {
if(stack[stack.length - 1] !== pairs.get(ch) || !stack.length) {
return false
}
stack.pop()
} else {
stack.push(ch)
}
}
return !stack.length
};
题目总结
这类题目的重点在于首先搞清楚顺序,队列fifo(先进先出),栈filo(后进先出),这几道题目实际上难度并不大,但搞清楚题意和知悉想要考察的点比较重要~