Given the running logs of n functions that are executed in a nonpreemptive single threaded CPU, find the exclusive time of these functions.
Each function has a unique id, start from 0 to n-1. A function may be called recursively or by another function.
A log is a string has this format : function_id:start_or_end:timestamp
. For example, "0:start:0"
means function 0 starts from the very beginning of time 0. "0:end:0"
means function 0 ends to the very end of time 0.
Exclusive time of a function is defined as the time spent within this function, the time spent by calling other functions should not be considered as this function's exclusive time. You should return the exclusive time of each function sorted by their function id.
Example 1:
Input: n = 2 logs = ["0:start:0", "1:start:2", "1:end:5", "0:end:6"] Output:[3, 4] Explanation: Function 0 starts at time 0, then it executes 2 units of time and reaches the end of time 1. Now function 0 calls function 1, function 1 starts at time 2, executes 4 units of time and end at time 5. Function 0 is running again at time 6, and also end at the time 6, thus executes 1 unit of time. So function 0 totally execute 2 + 1 = 3 units of time, and function 1 totally execute 4 units of time.
Note:
- Input logs will be sorted by timestamp, NOT log id.
- Your output should be sorted by function id, which means the 0th element of your output corresponds to the exclusive time of function 0.
- Two functions won't start or end at the same time.
- Functions could be called recursively, and will always end.
- 1 <= n <= 100
這道題讓我們函數的獨家運行的時間,沒錯,exclusive就是要翻譯成獨家,要讓每個函數都成為碼農的獨家記憶~哈~根據題目中給的例子,我們可以看出來,當一個函數start了之后,並不需要必須有end,可以直接被另一個程序start的時候強行關閉。而且,在某個時間點上調用end時,也不需要前面非得調用start,可以直接在某個時間點來個end,這樣也算執行了1秒,得+1秒~咳咳,本站禁“苟”,請勿輕易吟詩。博主自以為了解了這個題的邏輯,自己寫了一個,結果跪在了下面這個test case:
2
["0:start:0","0:start:2","0:end:5","1:start:7","1:end:7","0:end:8"]
Expected:
[8,1]
這個結果很confusing啊,你想啊,函數0運行了時間點0,1,2,3,4,5,8,共7秒,函數1運行了時間點7,共1秒,為啥答案不是[7,1]而是[8,1]呢?
根據分析網上大神們的解法,貌似時間點6還是函數0在執行。這是為啥呢,說明博主之前的理解有誤,當函數0在時間點2時再次開啟時,前面那個函數0應該沒有被強制關閉,所以現在實際上有兩個函數0在執行,所以當我們在時間點5關掉一個函數0時,還有另一個函數0在跑,所以時間點6還是函數0的,還得給函數0續1秒。這樣才能解釋的通這個case啊。這樣的話用棧stack就比較合適了,函數開啟了就壓入棧,結束了就出棧,不會有函數被漏掉。這樣的我們可以遍歷每個log,然后把三部分分開,函數idx,類型type,時間點time。如果此時棧不空,說明之前肯定有函數在跑,那么不管當前時start還是end,之前函數時間都得增加,增加的值為time - preTime,這里的preTime是上一個時間點。然后我們更新preTime為當前時間點time。然后我們判斷log的類型,如果是start,我們將當前函數壓入棧;如果是end,那么我們將棧頂元素取出,對其加1秒,並且preTime也要加1秒,參見代碼如下:
解法一:
class Solution { public: vector<int> exclusiveTime(int n, vector<string>& logs) { vector<int> res(n, 0); stack<int> st; int preTime = 0; for (string log : logs) { int found1 = log.find(":"); int found2 = log.find_last_of(":"); int idx = stoi(log.substr(0, found1)); string type = log.substr(found1 + 1, found2 - found1 - 1); int time = stoi(log.substr(found2 + 1)); if (!st.empty()) { res[st.top()] += time - preTime; } preTime = time; if (type == "start") st.push(idx); else { auto t = st.top(); st.pop(); ++res[t]; ++preTime; } } return res; } };
下面這種方法比較叼的地方是在於使用了C語言的sscanf函數來一步讀取了三個變量,注意這里面的"[^:]",表示copy所有字符,直到遇到':',這樣就能把中間的start或者end拷到type中去了。而且接下來的寫法跟上面也不太相同,這里先判斷了type的類型,如果是start,那么再看如果棧不為空,那么棧頂函數加上時間差,這個上面講過了,然后將當前函數壓入棧;如果是end,那么棧頂元素加上時間差,還要再加1秒,這個在上面也提到了加了1秒的事,然后再將棧頂元素出棧。最后更新preTime為當前時間點。講解中加了這么多秒,博主已經盡力了。。。
解法二:
class Solution { public: vector<int> exclusiveTime(int n, vector<string>& logs) { vector<int> res(n, 0); stack<int> st; int preTime = 0, idx = 0, time = 0; char type[10]; for (string log : logs) { sscanf(log.c_str(), "%d:%[^:]:%d", &idx, type, &time); if (type[0] == 's') { if (!st.empty()) { res[st.top()] += time - preTime; } st.push(idx); } else { res[st.top()] += ++time - preTime; st.pop(); } preTime = time; } return res; } };
參考資料:
https://discuss.leetcode.com/topic/96120/simple-c-using-stack
https://discuss.leetcode.com/topic/96068/java-stack-solution-o-n-time-o-n-space