LeetCode每日一题——函数的独占时间

437 阅读3分钟

LeetCode 第636题 函数的独占时间

题目:

给出一个非抢占单线程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

  1. 0:start:00:start:0入栈
  2. 1:start:21:start:2入栈
  3. 1:end:51:start:2出栈 计算函数1的耗时:5-2+1 = 4
  4. 0:end:60: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,提交的时候就递归案例就没过(其实不只是递归,只要是多层级嵌套的都会有问题),后来改成了让栈内所有都加。