这道题需要缓存当前正在执行的函数,用到栈。维护一个调用栈,在调用其他函数或者当前函数调用结束时更新栈。
遇到start
时,将函数入栈,表明调用开始;在遇到end
时,需要将栈顶函数弹出,表明调用结束。
以 ["0:start:0","0:start:2","0:end:5","1:start:6","1:end:6","0:end:7"]
为例:
- 一开始栈为空,遇到
0:start:0
,将函数0
入栈,表明开始调用
0:start:2
,表明是递归调用,将函数0
再次入栈,而此时外层的函数0
已执行的时间为2 - 0 = 2个时间单位
0:end:5
,函数0
结束调用,将此时栈顶的函数0
弹出,计算执行时间5 - 2 + 1 = 4
,所以函数0
此时总执行时间为4 + 2 = 6个时间单位
。递归的函数0
结束之后,恢复外层(栈顶)的函数0
的调用,同时需要更新栈顶函数的开始调用时间为上个函数调用的结束时间 + 1
,所以栈顶被修改成0: start: 6
1:start:6
,栈顶函数0
开始时间也是6
,这里可以理解成函数0
本身执行的结果是直接调用了函数1
,将函数1
压入栈中,函数1
开始调用
1:end:6
,函数1
结束调用,栈顶的函数1
弹出,计算总执行时间为6 - 6 + 1 = 1
个时间单位,所以这里的函数1
总的执行时间为1个时间单位
。同时修改栈顶函数0
的开始时间为当前执行函数1
的结束时间 + 1
,所以
-
0:end:7
,栈顶0
弹出,计算总时间为7 - 7 + 1 = 1
,加上之前的总执行时间为6个时间单位
,所以函数0总的执行时间为7个时间单位
-
所以
函数0
总执行时间为7
,函数1
的总执行时间为1
,结果数组为[7, 1]
,
具体代码:
function exclusiveTime(n: number, logs: string[]): number[] {
const times = new Array(n).fill(0)
let i = 0
const stack: [number, string, number][] = []
while (i < logs.length) {
// 这里的断言是为了让ts编译器不报错
// 数组的索引可以是string或者number,在减法运算时,字符串也会被隐式转换
const cur = logs[i].split(':') as [number, string, number]
if (cur[1] === 'start') {
// start时入栈
const top = stack[stack.length - 1]
if (top) {
times[top[0]] += +cur[2] - top[2]
}
stack.push(cur)
} else {
// end时出栈
const pre = stack.pop() as [number, string, number]
times[cur[0]] += +cur[2] - pre[2] + 1
// 最后一个end时,栈顶没有元素
if (stack.length > 0) {
// 更新栈顶函数开始时间
stack[stack.length - 1][2] = +cur[2] + 1
}
}
i++
}
return times
}