题目:
给出一个非抢占单线程CPU的 n 个函数运行日志,找到函数的独占时间。
每个函数都有一个唯一的 Id,从 0 到 n-1,函数可能会递归调用或者被其他函数调用。
日志是具有以下格式的字符串:function_id:start_or_end:timestamp。例如:0:start:0表示函数 0 从 0 时刻开始运行。0:end:0表示函数 0 在 0 时刻结束。
函数的独占时间定义是在该方法中花费的时间,调用其他函数花费的时间不算该函数的独占时间。你需要根据函数的 Id 有序地返回每个函数的独占时间。
示例1:
输入:
n = 2
logs =
["0:start:0",
"1:start:2",
"1:end:5",
"0:end:6"]
输出:
[3, 4]
说明:
函数 0 在时刻 0 开始,在执行了 2个时间单位结束于时刻 1。
现在函数 0 调用函数 1,函数 1 在时刻 2 开始,执行 4 个时间单位后结束于时刻 5。
函数 0 再次在时刻 6 开始执行,并在时刻 6 结束运行,从而执行了 1 个时间单位。
所以函数 0 总共的执行了 2 +1 =3 个时间单位,函数 1 总共执行了 4 个时间单位。
提示:
很多人(包括我)第一次读题的时候有点懵逼,为啥函数1从2开始5结束,耗时是4?如果计算方式是5-2+1 = 4,那为什么函数0的耗时不是2-0+1 + 6-5+1 = 4呢?
后来看了评论区说可以看看英文题目,英文题目中有一张便于理解的图:
看了图片之后就很好理解了,0-6总共有7个时间片函数1从2到5一共用了4个时间片,函数0则用了剩余的3个时间片。
思路1:
因为后执行的函数先结束,这种后进先出的方法很自然就联想到使用栈来解题。
我的解题思路是模仿栈内存。步骤大致如下:
- 创建一个栈。
- 每有一个函数启动了,就压入栈。
- 当函数结束时,弹出栈。
- 弹出栈的时候将该函数的耗时添加到数组中。
这里前三步很简单,最主要的就是如何计算函数的耗时。
以示例为例,先遍历执行logs:
0:start:0:0:start:0入栈1:start:2:1:start:2入栈1:end:5:1:start:2出栈 计算函数1的耗时:5-2+1 = 40:end:6:0:start:0出栈 计算函数0的耗时: 6-0+1 - 4 =3
注意:这里要减掉函数1的耗时
在代码设计时,为了方便计算,我直接在第三步的时候,将函数1的耗时加在了栈顶元素的起始值里,可以理解为:函数0在函数1执行完之后才执行的。如下图:
var exclusiveTime = function(n, logs) {
let res = Array(n).fill(0),stack = [];
for(let log of logs){
let q = log.split(':');
q[0] = parseInt(q[0]);
q[2] = parseInt(q[2]);
//如果是开始,则入栈
if(q[1]=="start"){
//这里做了优化,只存了起始时间
stack.push( q[2] );
continue;
}
//如果是end,弹出栈顶元素,开始计算
let p = stack.pop();
let len = q[2] - p + 1;
//len为函数q[0]的耗时
res[q[0]] += len;
//如果栈内还有值,让栈顶函数的起始值增加len
//if(stack.length>0) stack[stack.length-1][1] += len;
//栈内所有函数的起始值都增加len
for(let i=0;i<stack.length;i++){
stack[i] += len;
}
}
return res;
};
最开始我只考虑的一层嵌套,所以只给栈顶元素的起始值加了len,提交的时候就递归案例就没过(其实不只是递归,只要是多层级嵌套的都会有问题),后来改成了让栈内所有都加。