做题记录——栈

1.lc636:函数的独占时间

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/exclusive-time-of-functions

题目:有一个 单线程 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 的函数的独占时间。

示例
输入:n = 2, logs = ["0:start:0","1:start:2","1:end:5","0:end:6"]
输出:[3,4]
解释:
函数 0 在时间戳 0 的起始开始执行,执行 2 个单位时间,于时间戳 1 的末尾结束执行。 
函数 1 在时间戳 2 的起始开始执行,执行 4 个单位时间,于时间戳 5 的末尾结束执行。 
函数 0 在时间戳 6 的开始恢复执行,执行 1 个单位时间。 
所以函数 0 总共执行 2 + 1 = 3 个单位时间,函数 1 总共执行 4 个单位时间。 

思路:用栈模拟函数的调用,栈内存放的是函数的标识符和时间戳。如果是开始,就入栈,此时如果栈不为空,要计算原栈顶的执行时间;如果是结束,弹出栈顶,计算执行时间,此时如果栈不为空,更新栈顶的时间戳。

ps:一个小的注意点是计算问题(一直都很讨厌时间的计算)。由于开始是在时间戳的起始,结束是在时间戳的末尾,所以在结束的时候计算执行时间记得加一,以及栈顶的新时间戳也是结束时间戳加一。

代码

class Solution {
public:
    vector<int> exclusiveTime(int n, vector<string>& logs) {
        vector<int>ans(n);
        int index,time,len,i;
        stack<pair<int,int>>st;
        pair<int,int>pii;
        bool isbegin;
        for(string &s:logs){
            len=s.length();
            index=time=0;
            for(i=0;i<len;i++){
                if(s[i]==':')break;
                index=index*10+s[i]-'0';
            }
            if(s[++i]=='s')isbegin=true;
            else isbegin=false;
            for(;i<len;i++){
                if(s[i]==':')break;
            }
            for(i=i+1;i<len;i++){
                time=time*10+s[i]-'0';
            }
            if(isbegin){
                if(!st.empty()){
                    pii=st.top();
                    ans[pii.first]+=time-pii.second;
                }
                pii.first=index;
                pii.second=time;
                st.push(pii);
            }
            else{
                pii=st.top();
                st.pop();
                ans[index]+=time-pii.second+1;//记得加一
                if(!st.empty()){
                    pii=st.top();
                    st.pop();
                    pii.second=time+1;//这里也是
                    st.push(pii);
                }
            }
        }
        return ans;
    }
};

补充:c++的string没有split函数(可恶),所以我就手动分割字符串了,后来在题解里看到了sscanf函数。

char type[10];
int idx, timestamp;
sscanf(log.c_str(), "%d:%[^:]:%d", &idx, type, &timestamp);

作者:LeetCode-Solution
链接:https://leetcode.cn/problems/exclusive-time-of-functions/solution/han-shu-de-du-zhan-shi-jian-by-leetcode-d54e2/
来源:力扣(LeetCode)

2.lc768:最多能完成排序的块Ⅱ

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/max-chunks-to-make-sorted-ii

题目:这个问题和“最多能完成排序的块”相似,但给定数组中的元素可以重复,输入数组最大长度为2000,其中的元素最大为10**8。

arr是一个可能包含重复元素的整数数组,我们将这个数组分割成几个“块”,并将这些块分别进行排序。之后再连接起来,使得连接的结果和按升序排序后的原数组相同。

我们最多能将数组分成多少块?

示例 :

输入: arr = [5,4,3,2,1]
输出: 1
解释:
将数组分成2块或者更多块,都无法得到所需的结果。
例如,分成 [5, 4], [3, 2, 1] 的结果是 [4, 5, 1, 2, 3],这不是有序的数组。 

思路:【官方题解】

作者:LeetCode-Solution
链接:https://leetcode.cn/problems/max-chunks-to-make-sorted-ii/solution/zui-duo-neng-wan-cheng-pai-xu-de-kuai-ii-w5c6/
来源:力扣(LeetCode)

对于已经分好块的数组,若块数大于 1,则可以得到以下结论:右边的块的所有数字均大于或等于左边的块的所有数字。考虑这个问题:对于已经分好块的数组,若在其末尾添加一个数字,如何求得新数组的分块方式?

新添加的数字可能会改变原数组的分块方式。如果新添加的数字大于或等于原数组最后一个块的最大值,则这个新添加的数字可以自己形成一个块。如果新添加的数字小于原数组最后一个块的最大值,则它必须融入最后一个块。如果它大于或等于原数组倒数第二个块(如果有)的最大值,那么这个过程可以停止,新数组的分块方式已经求得。否则,它将继续融合原数组倒数第二个块,直到遇到一个块,使得该块的最大值小于或等于这个新添加的数,或者这个数字已经融合了所有块。

上述分析过程中,我们只用到了块的最大值来进行比较,比较过程又是从右到左,符合栈的思想,因此可以用类似单调栈的数据结构来存储块的最大值。

代码

class Solution {
public:
    int maxChunksToSorted(vector<int>& arr) {
        stack<int> st;
        for (auto &num : arr) {
            if (st.empty() || num >= st.top()) {
                st.emplace(num);
            } else {
                int mx = st.top();
                st.pop();
                while (!st.empty() && st.top() > num) {
                    st.pop();
                }
                st.emplace(mx);
            }
        }
        return st.size();
    }
};

ps:该题的哈希表做法请见做题记录——哈希表_Kephenny的博客-CSDN博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值