1.題目
題目地址(621. 任務排程器 - 力扣(LeetCode))
https://leetcode.cn/problems/task-scheduler/
題目描述
給你一個用字元陣列 tasks
表示的 CPU 需要執行的任務列表,用字母 A 到 Z 表示,以及一個冷卻時間 n
。每個週期或時間間隔允許完成一項任務。任務可以按任何順序完成,但有一個限制:兩個 相同種類 的任務之間必須有長度為 n
的冷卻時間。
返回完成所有任務所需要的 最短時間間隔 。
示例 1:
輸入:tasks = ["A","A","A","B","B","B"], n = 2 輸出:8 解釋:A -> B -> (待命) -> A -> B -> (待命) -> A -> B 在本示例中,兩個相同型別任務之間必須間隔長度為 n = 2 的冷卻時間,而執行一個任務只需要一個單位時間,所以中間出現了(待命)狀態。
示例 2:
輸入:tasks = ["A","A","A","B","B","B"], n = 0 輸出:6 解釋:在這種情況下,任何大小為 6 的排列都可以滿足要求,因為 n = 0 ["A","A","A","B","B","B"] ["A","B","A","B","A","B"] ["B","B","B","A","A","A"] ... 諸如此類
示例 3:
輸入:tasks = ["A","A","A","A","A","A","B","C","D","E","F","G"], n = 2 輸出:16 解釋:一種可能的解決方案是: A -> B -> C -> A -> D -> E -> A -> F -> G -> A -> (待命) -> (待命) -> A -> (待命) -> (待命) -> A
提示:
1 <= tasks.length <= 104
tasks[i]
是大寫英文字母0 <= n <= 100
2.題解
2.1 貪心演算法 + 優先順序佇列
思路
這題的關鍵思路是我們需要明白具體的貪心策略:
當處於某一個時間點,這裡有若干個未被冷卻的任務等待執行,我們應該優先執行那些剩餘次數較多的任務
因為其餘剩餘次數較少的任務,我們可以穿插到其他的時間間隔進行執行,因為剩餘次數少,需要插空的位置少,更容易完成,
而那些剩餘次數多的任務,如果放到後面執行,可能就會面對只剩下這類任務的情況,我們執行一個該類任務,必須等待一整個時間間隔n什麼都不能做
所以我們的貪心策略:優先執行當前未處於冷卻狀態,且剩餘執行次數較多的任務
後面一個比較好解決,使用優先順序佇列(大根堆)即可解決,我們如何解決前面一個限制呢?
我們發現我們如果是以一個冷卻週期n來劃分任務執行,那麼假設這個冷卻週期我執行了若干任務,並執行一個從優先順序佇列中排除一個;
並在這個冷卻週期完畢後(指第一個任務,中間其他任務尚未完成冷卻),將所有本週期執行過的,被排除的任務全部裝回優先順序佇列,並執行下一個週期;
我們可能有一個疑問,此時優先順序佇列中不是所有任務都冷卻了(上一個週期執行的後幾個任務都未冷卻),我們直接放回開啟新週期可以嗎?
答案是可以的,在下一個週期中,我們還是按,有這麼幾種可能:
1.這裡執行的任務上個冷卻週期我沒執行過(由於上一週期任務執行,部分任務執行次數減少後小於該任務,導致該任務優先順序上升),那麼自然沒有冷卻問題的
(關鍵!!!)2.這裡執行的任務上個冷卻週期我執行過(但是根據上一週期中優先順序高於你的任務,你們的剩餘次數同時-1,其實這一週期還是按次序還是排在你前面的,等他執行完,你也就冷卻好了!!!)
所以我們就可以採用一個冷卻週期一輪迴的方式來解決這個問題,一個輪迴中:
1.執行n+1個任務(自己執行的一次 + 冷卻時間的n次),並排除優先順序佇列
2.執行完畢後,將這些任務放回優先順序佇列
3.更新總執行時間
程式碼
- 語言支援:C++
C++ Code:
class Solution {
public:
int leastInterval(std::vector<char>& tasks, int n) {
// 統計任務出現頻率
unordered_map<char, int> taskFreq;
for(char ch : tasks){
if(taskFreq.count(ch)){
taskFreq[ch]++;
}else{
taskFreq.emplace(ch, 1);
}
}
// 使用優先順序佇列-維護剩餘次數從大到小排列(大根堆)
priority_queue<int> maxheap;
for(auto p : taskFreq){
maxheap.push(p.second);
}
int ans = 0;
// 開始時間流動,每次迴圈一個時間冷卻時間
while(!maxheap.empty()){
vector<int> temp;
// 執行當前剩餘次數最多的任務(一個週期:自己執行的一個間隔 + 冷卻室家n)
for(int i = 0; i < n + 1; i++){
if(!maxheap.empty()){
temp.push_back(maxheap.top() - 1);
maxheap.pop();
}
}
// 執行完一輪後,將未完成的任務放回優先順序佇列中
for(int tmp : temp){
if(tmp > 0){
maxheap.push(tmp);
}
}
// 更新完成當前任務總的時間間隔
// 如果棧為空,說明最後temp中的值均為0,所有任務執行完畢,加上這一部分即可
if(maxheap.empty()){
ans += temp.size();
}else{
ans += n + 1;
}
}
return ans;
}
};
複雜度分析
- 時間複雜度:'o(w* log K),其中'N 是任務的總數量,'K 是任務種類的數量。
對於每個任務操作,堆的操作(插入和刪除)是“o(log K)”的。 - 空間複雜度:‘’o(k)”,主要用於儲存任務頻率的雜湊表和優先順序佇列。