- 682. 棒球比赛
- 232. 用栈实现队列
- 844. 比较含退格的字符串
- 946. 验证栈序列
- 20. 有效的括号
- 1249. 移除无效的括号
- 1021. 删除最外层的括号
- 1124. 表现良好的最长时间段
- 227. 基本计算器 II
- 636. 函数的独占时间
682. 棒球比赛
你现在是一场采用特殊赛制棒球比赛的记录员。这场比赛由若干回合组成,过去几回合的得分可能会影响以后几回合的得分。
比赛开始时,记录是空白的。你会得到一个记录操作的字符串列表 ops,其中 ops[i] 是你需要记录的第 i 项操作,ops 遵循下述规则:
整数 x – 表示本回合新获得分数 x
“+” – 表示本回合新获得的得分是前两次得分的总和。题目数据保证记录此操作时前面总是存在两个有效的分数。
“D” – 表示本回合新获得的得分是前一次得分的两倍。题目数据保证记录此操作时前面总是存在一个有效的分数。
“C” – 表示前一次得分无效,将其从记录中移除。题目数据保证记录此操作时前面总是存在一个有效的分数。
请你返回记录中所有得分的总和。
提示:
1 <= ops.length <= 1000
ops[i] 为 “C”、”D”、”+”,或者一个表示整数的字符串。整数范围是 [-3 * 104, 3 * 104]
对于 “+” 操作,题目数据保证记录此操作时前面总是存在两个有效的分数
对于 “C” 和 “D” 操作,题目数据保证记录此操作时前面总是存在一个有效的分数
题意理解:
给定一个数组,根据特定规则计算得分
可数情况不考虑
- 数组中的数据为 符号”+”、字母”D”、字母”C”、和字符串数字组成
- 声明一个数字,用来保存每次操作后的结果
- 遍历数组,如果为”C”则删除上个元素
- 如果为”D” 当前数等于上个数字*2
- 如果为”+” 当前数等于前两个数之和
- 其他转为数组存储
/**
* @param {string[]} ops
* @return {number}
*/
var calPoints = function(ops) {
const arr=[]
for(let val of ops){
if(val==="C"){
arr.pop()
}else if(val==="D"){
arr.push(arr[arr.length-1]*2)
}else if(val==="+"){
arr.push(arr[arr.length-1]+arr[arr.length-2])
}else{
arr.push(Number(val))
}
}
return arr.reduce((x,y)=>x+y,0)
};
复制代码
232. 用栈实现队列
请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作(push、pop、peek、empty):
实现 MyQueue 类:
void push(int x) 将元素 x 推到队列的末尾
int pop() 从队列的开头移除并返回元素
int peek() 返回队列开头的元素
boolean empty() 如果队列为空,返回 true ;否则,返回 false
说明:
你 只能 使用标准的栈操作 —— 也就是只有 push to top, peek/pop from top, size, 和 is empty 操作是合法的。
你所使用的语言也许不支持栈。你可以使用 list 或者 deque(双端队列)来模拟一个栈,只要是标准的栈操作即可。
提示:
1 <= x <= 9
最多调用 100 次 push、pop、peek 和 empty
假设所有操作都是有效的 (例如,一个空的队列不会调用 pop 或者 peek 操作)
提议理解:
使用两个栈模拟队列(栈 先进后出,队列 先进先出)
- 要实现入队、出队、判断队列是否为空、返回队首元素的方法
- 一个栈用来存储每次入队的操作
- 一个栈用来存来存储出队的操作,如果出队的栈为空,则遍历入栈的栈,放进出栈的栈中,如果两个都为空则为空
var MyQueue = function() {
this.queue=[];
this.stack=[];
};
/**
* @param {number} x
* @return {void}
*/
MyQueue.prototype.push = function(x) {
this.stack.push(x)
};
/**
* @return {number}
*/
MyQueue.prototype.pop = function() {
if(!this.queue.length){
while(this.stack.length){
this.queue.push(this.stack.pop())
}
}
return this.queue.pop()
};
/**
* @return {number}
*/
MyQueue.prototype.peek = function() {
if(!this.queue.length){
while(this.stack.length){
this.queue.push(this.stack.pop())
}
}
return this.queue[this.queue.length-1]
};
/**
* @return {boolean}
*/
MyQueue.prototype.empty = function() {
return !this.queue.length && !this.stack.length
};
/**
* Your MyQueue object will be instantiated and called as such:
* var obj = new MyQueue()
* obj.push(x)
* var param_2 = obj.pop()
* var param_3 = obj.peek()
* var param_4 = obj.empty()
*/
复制代码
844. 比较含退格的字符串
给定 s 和 t 两个字符串,当它们分别被输入到空白的文本编辑器后,如果两者相等,返回 true 。# 代表退格字符。
注意:如果对空文本输入退格字符,文本继续为空。
提示:
1 <= s.length, t.length <= 200
s 和 t 只含有小写字母以及字符 ‘#’
题意理解:
给定一个字符串,出现”#”代表删除前一个字符,判断两个字符串执行操作后是否相等
使用栈进行求解
- 声明两个栈来存储两个字符串执行后的最终结果,若果相等则返回true,否则返回false
- 遍历单个字符串,如果为#且站不为null则执行出栈曹邹,否则执行入栈操作
/**
* @param {string} s
* @param {string} t
* @return {boolean}
*/
var backspaceCompare = function (s, t) {
function stack(str) {
const arr = []
for (let i = 0; i < str.length; i++) {
if (str[i] === "#") {
if(!arr.length)continue;
arr.pop()
} else {
arr.push(str[i])
}
}
return arr;
}
let a = stack(s);
let b = stack(t);
while(a.length && b.length){
if(a.pop()!==b.pop()){
return false
}
}
return !a.length && !b.length
};
复制代码
字符串操作
- 声明变量a和b用来保存s,t操作完成后的值
/**
* @param {string} s
* @param {string} t
* @return {boolean}
*/
var backspaceCompare = function(s, t) {
let a="";
let b="";
for(let i=0;i<s.length;i++){
if(s[i]==="#"){
a=a.slice(0,-1)
}else{
a+=s[i]
}
}
for(let i=0;i<t.length;i++){
if(t[i]==="#"){
b=b.slice(0,-1)
}else{
b+=t[i]
}
}
return a===b;
};
复制代码
946. 验证栈序列
给定 pushed 和 popped 两个序列,每个序列中的 值都不重复,只有当它们可能是在最初空栈上进行的推入 push 和弹出 pop 操作序列的结果时,返回 true;否则,返回 false 。
提示:
1 <= pushed.length <= 1000
0 <= pushed[i] <= 1000
pushed 的所有元素 互不相同
popped.length == pushed.length
popped 是 pushed 的一个排列
题意理解:
判断popped是否为pushed出栈的一种结果
- 设置变量i表示popped中第i个数,stack模拟入栈出栈
- 遍历pushed,当前值入栈stack, 如果当前值和popped[i]相同,则执行出栈stack,并且i++,遍历栈stack如果栈顶和popped[i]形同,则stack出栈i++
- 遍历结束,如果stack为null则返回true,否则返回false
/**
* @param {number[]} pushed
* @param {number[]} popped
* @return {boolean}
*/
var validateStackSequences = function (pushed, popped) {
const stack = [];
let i = 0;
for (const val of pushed) {
stack.push(val)
if (popped[i] === val) {
stack.pop()
i++;
while (stack.length && popped[i] === stack[stack.length - 1]) {
stack.pop()
i++;
}
}
}
return !stack.length
};
复制代码
20. 有效的括号
给定一个只包括 ‘(‘,’)’,'{‘,’}’,'[‘,’]’ 的字符串 s ,判断字符串是否有效。
有效字符串需满足:
左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
提示:
1 <= s.length <= 104
s 仅由括号 ‘()[]{}’ 组成
题意理解:
判断给定的字符串中的括号是否符合闭合条件
- 声明一个栈stack用来匹配括号
- 如果为左括号,入栈
- 否则出栈,判断出栈的括号是否和当前括号匹配,不匹配返回false
- 最后如果stack为null则返回true,否则返回false
/**
* @param {string} s
* @return {boolean}
*/
var isValid = function (s) {
const stack = [];
for (let i = 0; i < s.length; i++) {
if (s[i] === "(" || s[i] === "[" || s[i] === "{") {
stack.push(s[i])
} else {
const current = stack.pop()
if (!(current === "(" && s[i] === ")") && !(current === "[" && s[i] === "]") && !(current === "{" && s[i] === "}")) {
return false
}
}
}
return !stack.length;
};
复制代码
1249. 移除无效的括号
给你一个由 ‘(‘、’)’ 和小写字母组成的字符串 s。
你需要从字符串中删除最少数目的 ‘(‘ 或者 ‘)’ (可以删除任意位置的括号),使得剩下的「括号字符串」有效。
请返回任意一个合法字符串。
有效「括号字符串」应当符合以下 任意一条 要求:
空字符串或只包含小写字母的字符串
可以被写作 AB(A 连接 B)的字符串,其中 A 和 B 都是有效「括号字符串」
可以被写作 (A) 的字符串,其中 A 是一个有效的「括号字符串」
提示:
1 <= s.length <= 105
s[i] 可能是 ‘(‘、’)’ 或英文小写字母
题意理解:
输出有效的字符串,及括号可以配对,不匹配的删除掉
- 声明两个栈stack保存不匹配的”(“的下标,deleteIndex保存不匹配的”)”的下标
- str保存返回值
/**
* @param {string} s
* @return {string}
*/
var minRemoveToMakeValid = function (s) {
const stack = [];
let deleteIndex = [];
let str = "";
for (let i = 0; i < s.length; i++) {
if (s[i] === "(") {
stack.push(i)
} else if (s[i] === ")" && stack.length) {
stack.pop();
} else if (s[i] === ")") {
deleteIndex.push(i)
}
}
deleteIndex=[...deleteIndex,...stack]
for (let i = 0; i < s.length; i++) {
if(deleteIndex.includes(i)){
continue;
}
str+=s[i];
}
return str;
};
复制代码
1021. 删除最外层的括号
有效括号字符串为空 “”、”(” + A + “)” 或 A + B ,其中 A 和 B 都是有效的括号字符串,+ 代表字符串的连接。
例如,””,”()”,”(())()” 和 “(()(()))” 都是有效的括号字符串。
如果有效字符串 s 非空,且不存在将其拆分为 s = A + B 的方法,我们称其为原语(primitive),其中 A 和 B 都是非空有效括号字符串。
给出一个非空有效字符串 s,考虑将其进行原语化分解,使得:s = P_1 + P_2 + … + P_k,其中 P_i 是有效括号字符串原语。
对 s 进行原语化分解,删除分解中每个原语字符串的最外层括号,返回 s 。
提示:
1 <= s.length <= 105
s[i] 为 ‘(‘ 或 ‘)’
s 是一个有效括号字符串
提议理解:
删除最外层的没对括号(一层)
- count左括号数,left起始位置
- 当count为0时表示一对凑齐,接着遍历下一个
/**
* @param {string} s
* @return {string}
*/
var removeOuterParentheses = function(s) {
let count=0;
let left=0;
let str="";
for(let i=0;i<s.length;i++){
if(s[i]==="("){
count++;
}else{
count--;
}
if(count===0){
str+=s.slice(left+1,i)
left=i+1;
}
}
return str
};
复制代码
1124. 表现良好的最长时间段
给你一份工作时间表 hours,上面记录着某一位员工每天的工作小时数。
我们认为当员工一天中的工作小时数大于 8 小时的时候,那么这一天就是「劳累的一天」。
所谓「表现良好的时间段」,意味在这段时间内,「劳累的天数」是严格 大于「不劳累的天数」。
请你返回「表现良好时间段」的最大长度
提示:
1 <= hours.length <= 104
0 <= hours[i] <= 16
题意理解:
找出一个最长时间段劳累天数大于不劳累天数的总天数
- 声明arr用来存储当前天多余劳累的天数
- 放最后一个数大于0时表示整体都是劳累的,之间返回数组长度
- res存储当前最大劳累天数
- 遍历arr,找出当前劳累天数和第一个劳累天数相同的长度的差,及此时的劳累天数
- 返回res
/**
* @param {number[]} hours
* @return {number}
*/
var longestWPI = function(hours) {
const arr=[];
let count=0;
for(let item of hours){
if(item>8){
arr.push(++count)
}else{
arr.push(--count)
}
}
if(arr[arr.length-1]>0) return arr.length;
let res=0;
let arr1=new Map();
for(let i=0;i<arr.length;i++){
if(!arr1.has(arr[i])) arr1.set(arr[i],i);
if(arr1.has(arr[i]-1)) {
res=Math.max(res,i-arr1.get(arr[i]-1));
}
if(arr[i]>0) res=Math.max(res,i+1);
}
return res;
};
复制代码
227. 基本计算器 II
给你一个字符串表达式 s ,请你实现一个基本计算器来计算并返回它的值。
整数除法仅保留整数部分。
你可以假设给定的表达式总是有效的。所有中间结果将在 [-231, 231 – 1] 的范围内。
注意:不允许使用任何将字符串作为数学表达式计算的内置函数,比如 eval() 。
提示:
1 <= ops.length <= 1000
ops[i] 为 “C”、”D”、”+”,或者一个表示整数的字符串。整数范围是 [-3 * 104, 3 * 104]
对于 “+” 操作,题目数据保证记录此操作时前面总是存在两个有效的分数
对于 “C” 和 “D” 操作,题目数据保证记录此操作时前面总是存在一个有效的分数
提议理解:
字符串是一个数学表达式,遍历字符串求出值,不使用内置方法
- 初始化栈stack保存当前值,只存在加减乘除
- sym表示上个字符默认为+ 正数
- num为当前数字,可能有多位
- 遍历字符串如果符号位+号则当前数入栈;如果为-号当前数入栈为负数;如果为*出栈数与当前数相乘结果入栈;如果为/号,出栈数/当前数,结果入栈
- 出栈数累加就是结果
/**
* @param {string} s
* @return {number}
*/
var calculate = function (s) {
s = s.trim();
let sym = "+";
const stack = [];
let num = 0;
for (let i = 0; i < s.length; i++) {
const isNumber = !isNaN(Number(s[i]));
if (isNumber && s[i] !== " ") {
num = num * 10 + s[i] * 1
}
if (!isNumber || i === s.length - 1) {
switch (sym) {
case '+':
stack.push(num);
break;
case '-':
stack.push(-num);
break;
case '*':
stack.push(stack.pop() * num);
break;
default:
const cNum = stack.pop();
stack.push(cNum > 0 ? Math.floor(cNum / num) : Math.ceil(cNum / num));
break;
}
num = 0;
sym = s[i];
}
}
let sum = 0;
while (stack.length) {
// 如果换成 shift 超出时间限制
sum += stack.pop();
}
return sum;
};
复制代码
636. 函数的独占时间
有一个 单线程 CPU 正在运行一个含有 n 道函数的程序。每道函数都有一个位于 0 和 n-1 之间的唯一标识符。
函数调用 存储在一个 调用栈 上 :当一个函数调用开始时,它的标识符将会推入栈中。而当一个函数调用结束时,它的标识符将会从栈中弹出。标识符位于栈顶的函数是 当前正在执行的函数 。每当一个函数开始或者结束时,将会记录一条日志,包括函数标识符、是开始还是结束、以及相应的时间戳。
给你一个由日志组成的列表 logs ,其中 logs[i] 表示第 i 条日志消息,该消息是一个按 “{function_id}:{“start” | “end”}:{timestamp}” 进行格式化的字符串。例如,”0:start:3″ 意味着标识符为 0 的函数调用在时间戳 3 的 起始开始执行 ;而 “1:end:2” 意味着标识符为 1 的函数调用在时间戳 2 的 末尾结束执行。注意,函数可以 调用多次,可能存在递归调用 。
函数的 独占时间 定义是在这个函数在程序所有函数调用中执行时间的总和,调用其他函数花费的时间不算该函数的独占时间。例如,如果一个函数被调用两次,一次调用执行 2 单位时间,另一次调用执行 1 单位时间,那么该函数的 独占时间 为 2 + 1 = 3 。
以数组形式返回每个函数的 独占时间 ,其中第 i 个下标对应的值表示标识符 i 的函数的独占时间。
提示:
1 <= n <= 100
1 <= logs.length <= 500
0 <= function_id < n
0 <= timestamp <= 109
两个开始事件不会在同一时间戳发生
两个结束事件不会在同一时间戳发生
每道函数都有一个对应 “start” 日志的 “end” 日志
提议理解:
分段计算cpu运行各个函数所使用的时间,一个函数可能运行多次
- res保存返回数据,stack保存开始的进程
- 遍历日志,如果类型为start则入栈,end为出栈,记录前一个进程运行时间,并且对不存在的函数初始化时间
方案二
/**
* @param {number} n
* @param {string[]} logs
* @return {number[]}
*/
var exclusiveTime = function (n, logs) {
const res = [];
const stack = [];
let prev = 0;
for (let item of logs) {
let [index, type, value] = item.split(":");
index *= 1;
value *= 1;
if (type === "start") {
if (res[index] === undefined) {
res[index] = 0;
}
if (stack.length) {
res[stack[stack.length - 1]] += value - prev - 1;
}
stack.push(index)
} else {
const currentIndex = stack.pop();
res[currentIndex] += value - prev + 1;
}
prev = value;
}
return res
};
复制代码
方案一
通过调试多次测试用例,最后调试出来的方法
第一次没考虑周全
- res存储结果
- stack存储为结束的进程和上个任务
- 如果此次为开始,则入栈,并计算上个任务的时长
- 否则出栈,计算时长
/**
* @param {number} n
* @param {string[]} logs
* @return {number[]}
*/
var exclusiveTime = function (n, logs) {
const res = [];
const stack = [];
for (let item of logs) {
const current = item.split(":");
if (current[1] === "start") {
if (res[current[0] * 1] === undefined) {
res[current[0] * 1] = 0;
}
if (stack.length) {
let pre = stack[stack.length - 1];
const diff = current[2] * 1 - pre[2] * 1;
if (pre[1] === "start") {
res[pre[0] * 1] += diff;
} else {
pre = stack.pop()
pre = stack.length ? stack[stack.length - 1] : pre;
res[pre[0] * 1] += diff - 1;
}
}
} else {
const pre = stack.pop();
res[current[0] * 1] += current[2] * 1 - pre[2] * 1;
if (pre[1] === "start") {
res[current[0] * 1] += 1;
}
// 特变注意点 pre[1] !== stack[stack.length - 1][1]
if (stack.length && pre[1] !== stack[stack.length - 1][1] && stack[stack.length - 1][0] === current[0]) {
stack.pop();
}
}
stack.push(current)
}
return res
};
复制代码