Task Scheduler und Gas Station
Wenden Sie Greedy-Überlegungen auf das Problem der Abkühlzeit im CPU-Task-Scheduler und auf das Problem der Machbarkeit einer Rundfahrt durch Tankstellen an.
Task Scheduler und Gas Station ist eine kostenlose Coding Interview Prep-Lektion auf CoddyKit. Dies ist Lektion 4 von 4. Du kannst die komplette Lektion unten kostenlos lesen – dann übst du sie direkt im Browser mit einem integrierten Code-Editor und einem KI-Tutor rund um die Uhr. Sie ist Teil des Coding Interview Prep-Lernpfads, und dein Fortschritt wird über Web und CoddyKit-App synchronisiert. Der Coding Interview Prep-Kurs umfasst insgesamt 4 Lektionen.
Task-Scheduler-Problem
Task Scheduler (LeetCode 621): Gegeben ist eine Liste von CPU-Aufgaben (jeweils mit einem Label von A bis Z) und eine Abkühlzeit n. Ermitteln Sie die minimale Anzahl von CPU-Intervallen, die zum Ausführen aller Aufgaben benötigt wird. Zwischen zwei Ausführungen derselben Aufgabe müssen mindestens n Intervalle liegen. Leerlaufintervalle sind erlaubt. Für die Aufgaben ['A','A','A','B','B','B'] mit der Abkühlzeit 2 lautet die Antwort 8: A→B→idle→A→B→idle→A→B.
# Task Scheduler example
tasks = ['A','A','A','B','B','B']
n = 2 # cooldown
# One optimal schedule: A B _ A B _ A B
# Intervals: 1 2 3 4 5 6 7 8 → answer = 8
# Another example: tasks=['A','A','A','B','B','C'] n=2
# A B C A B _ A → 7 intervals
print('Understanding the cooldown constraint')
print('Same task needs n intervals gap between runs')Greedy-Formel für den Task Scheduler
Die zentrale Erkenntnis: Die Gesamtdauer wird durch die am häufigsten vorkommende Aufgabe bestimmt. Wenn die häufigste Aufgabe f-mal vorkommt und es max_count Aufgaben mit der Häufigkeit f gibt, beträgt die Dauer max(len(tasks), (f-1) * (n+1) + max_count). Die Formel funktioniert so: Erzeugen Sie f-1 Rahmen der Größe n+1, füllen Sie sie mit anderen Aufgaben und fügen Sie den letzten Zyklus hinzu. Wenn andere Aufgaben alle Leerlaufplätze füllen (also viele verschiedene Aufgaben vorhanden sind), führen Sie einfach alle Aufgaben ohne Leerlauf aus.
from collections import Counter
def least_interval(tasks, n):
count = Counter(tasks)
max_freq = max(count.values())
# How many tasks have the maximum frequency?
max_count = sum(1 for c in count.values() if c == max_freq)
# Formula: max of total tasks (no idle) or frame-based calculation
frame_time = (max_freq - 1) * (n + 1) + max_count
return max(len(tasks), frame_time)
print(least_interval(['A','A','A','B','B','B'], 2)) # 8
print(least_interval(['A','A','A','B','B','B'], 0)) # 6 (no cooldown)
print(least_interval(['A','A','A','A','B','C'], 3)) # 10Warum die Formel funktioniert
Stellen Sie sich den Zeitplan als Raster mit n+1 Spalten vor (ein Platz für die Aufgabe plus n Abkühlplätze). Die am häufigsten vorkommende Aufgabe A (Häufigkeit f) benötigt f Zeilen. Zwischen dem ersten und dem letzten Vorkommen liegen f-1 vollständige Rahmen mit jeweils n+1 Plätzen. Hinzu kommt der letzte unvollständige Rahmen, der alle Aufgaben mit maximaler Häufigkeit enthält. Wenn genügend verschiedene Aufgaben vorhanden sind, füllen sie alle Leerlaufplätze, und die tatsächliche Anzahl der Aufgaben überschreitet die Dauer des Rasters — wählen Sie den größeren der beiden Werte.
# Visualise frame structure for AAABBB, n=2
# Frame size = n+1 = 3
# f = 3 (A appears 3 times), max_count = 2 (A and B both appear 3 times)
# Grid:
# [A B _] ← frame 1
# [A B _] ← frame 2
# [A B ] ← last partial frame (max_count=2 cells)
# Total = (3-1)*3 + 2 = 6 + 2 = 8
# If tasks = AAAABBCC, n=2: max_freq=4 (A), max_count=1
# (4-1)*(2+1)+1 = 9+1 = 10
# But len(tasks)=8 < 10, so answer is 10
tasks2 = ['A','A','A','A','B','B','C','C']
from collections import Counter
count = Counter(tasks2)
mf = max(count.values())
mc = sum(1 for c in count.values() if c == mf)
print(f'Frame formula: ({mf}-1)*{2+1}+{mc} = {(mf-1)*(2+1)+mc}')
print(f'Max(len={len(tasks2)}, frame={max(len(tasks2),(mf-1)*3+mc)}) = {max(len(tasks2),(mf-1)*3+mc)}')Alternative: Simulation mit einem Heap
Eine Heap-basierte Simulation erzeugt den tatsächlichen Zeitplan (nicht nur die Anzahl). Wählen Sie in jedem Schritt die verfügbare Aufgabe mit der höchsten Häufigkeit aus (Max-Heap). Wenden Sie nach der Ausführung die Abkühlzeit an: Fügen Sie die Aufgabe erst n Schritte später wieder ein. Verwenden Sie eine Warteschlange, um Aufgaben während der Abkühlzeit zu verwalten. Dies läuft in O(total_time × log k), wobei k die Anzahl verschiedener Aufgaben ist. Die Formel ist zwar schneller, liefert aber nicht den Zeitplan. Beherrschen Sie beide Ansätze — in Vorstellungsgesprächen kann nach dem Zeitplan selbst gefragt werden.
import heapq
from collections import deque, Counter
def task_scheduler_simulate(tasks, n):
count = Counter(tasks)
heap = [-c for c in count.values()] # max-heap using negation
heapq.heapify(heap)
time = 0
cooldown = deque() # (available_at, neg_count)
while heap or cooldown:
time += 1
if heap:
c = heapq.heappop(heap) + 1 # use one instance
if c < 0: # still has remaining tasks
cooldown.append((time + n, c))
if cooldown and cooldown[0][0] == time:
heapq.heappush(heap, cooldown.popleft()[1])
return time
print(task_scheduler_simulate(['A','A','A','B','B','B'], 2)) # 8Gas-Station-Problem
Gas Station (LeetCode 134): Es gibt n Tankstellen auf einem Kreis. Tankstelle i verfügt über gas[i] Kraftstoff, und die Fahrt zur nächsten Tankstelle kostet cost[i]. Starten Sie mit einem leeren Tank und ermitteln Sie die Tankstelle, an der Sie starten müssen, um die gesamte Runde zu absolvieren. Falls keine solche Tankstelle existiert, geben Sie -1 zurück. Das Problem garantiert, dass es höchstens eine gültige Lösung gibt, sofern eine existiert.
# Example:
gas = [1, 2, 3, 4, 5]
cost = [3, 4, 5, 1, 2]
# net gain per station: gas[i] - cost[i]
net = [g - c for g, c in zip(gas, cost)]
print('Net gain per station:', net) # [-2, -2, -2, 3, 3]
# Only possible start: station 3 (index 3)
# Tank: 0 +3=3 → 3-1=2 → 2+1=3-2=... let's verify
print('Sum of net:', sum(net)) # 1 > 0 means solution existsGreedy-Lösung für Gas Station
Greedy-Algorithmus: (1) Wenn die Gesamtmenge an Kraftstoff < die Gesamtkosten ist, existiert keine Lösung (geben Sie -1 zurück). (2) Andernfalls existiert genau eine Lösung. Finden Sie sie mit einem einzigen Durchlauf: Verwalten Sie tank (den aktuellen Kraftstoffvorrat) und start (die potenzielle Starttankstelle). Wenn nach dem Besuch einer Tankstelle tank < 0 gilt, kann der aktuelle Start diese Tankstelle nicht erreichen — setzen Sie tank = 0 zurück und start = i + 1. Der abschließende Wert von start ist die Antwort.
def can_complete_circuit(gas, cost):
if sum(gas) < sum(cost):
return -1 # impossible
tank = 0
start = 0
for i in range(len(gas)):
tank += gas[i] - cost[i]
if tank < 0:
tank = 0
start = i + 1 # current start failed, try next
return start
gas = [1, 2, 3, 4, 5]
cost = [3, 4, 5, 1, 2]
print(can_complete_circuit(gas, cost)) # 3
gas2 = [2, 3, 4]
cost2 = [3, 4, 3]
print(can_complete_circuit(gas2, cost2)) # -1Warum der Greedy-Start korrekt ist
Korrektheitsargument: Wenn der Tank nach dem Erreichen der Tankstelle i ausgehend von start leer ist, dann kann keine Tankstelle zwischen start und i (einschließlich) ein gültiger Startpunkt sein — beim Erreichen von Tankstelle i hätten Sie von jeder dieser Tankstellen aus weniger Kraftstoff als beim Start von start. Daher können wir sie sicher überspringen und i+1 ausprobieren. Da eine Lösung existiert (Gesamtkraftstoff ≥ Gesamtkosten), muss der abschließende Kandidat start funktionieren.
# Proof sketch: why start=i+1 is correct after tank<0 at station i
# If we start at station j (start <= j <= i), tank at j is tank_from_start(j)
# After stations start..j: tank_from_j starts at 0, but we've already used gas[start..j-1]
# Starting at j means: tank_at_i = sum(net[j..i]) = sum(net[start..i]) - sum(net[start..j-1])
# Since sum(net[start..i]) < 0 AND sum(net[start..j-1]) >= 0 (no reset before i),
# tank_at_i when starting at j is even more negative → j cannot work either
def verify_gas_solution(gas, cost, start):
tank = 0
n = len(gas)
for i in range(n):
idx = (start + i) % n
tank += gas[idx] - cost[idx]
if tank < 0: return False
return True
print(verify_gas_solution([1,2,3,4,5],[3,4,5,1,2], 3)) # TrueBrute Force und Greedy bei Gas Station
Brute Force probiert jede Starttankstelle aus und simuliert die gesamte Runde — Laufzeit O(n²). Die Greedy-Lösung mit einem einzigen Durchlauf benötigt O(n) Zeit und O(1) Speicher. Bei einem Array mit 10⁵ Tankstellen entspricht das 10¹⁰ Operationen gegenüber 10⁵. Die entscheidende mathematische Eigenschaft, die Greedy ermöglicht: Wenn die gesamte Nettokraftstoffmenge nicht negativ ist, existiert ein gültiger Startpunkt, und dieser ist immer die Tankstelle direkt nach dem letzten Punkt, an dem die laufende Summe negativ wurde.
def brute_force_gas(gas, cost):
n = len(gas)
for start in range(n):
tank = 0
valid = True
for i in range(n):
idx = (start + i) % n
tank += gas[idx] - cost[idx]
if tank < 0: valid = False; break
if valid: return start
return -1
def greedy_gas(gas, cost):
if sum(gas) < sum(cost): return -1
tank = start = 0
for i, (g, c) in enumerate(zip(gas, cost)):
tank += g - c
if tank < 0: tank = 0; start = i + 1
return start
gas = [1,2,3,4,5]; cost = [3,4,5,1,2]
print('Brute:', brute_force_gas(gas,cost), '== Greedy:', greedy_gas(gas,cost))Verwandt: Minimum Cost to Complete Trips
Minimum Time to Complete Trips (LeetCode 2187) ist ein Problem zur binären Suche über den Lösungsraum. Sie führen eine binäre Suche über den Zeitwert T durch: Bei einer gegebenen Zeit T absolvieren Busse mit time[i] jeweils floor(T/time[i]) Fahrten. Wenn die Gesamtzahl der Fahrten ≥ totalTrips ist, reicht T aus. Gesucht ist das kleinste solche T. Dies zeigt, dass Greedy-Strategien auf der Metaebene angewendet werden können, also durch binäre Suche über die Antworten, wenn es auf Objektebene keine direkte Greedy-Regel gibt.
def minimum_time(time, total_trips):
def can_complete(t):
return sum(t // bus for bus in time) >= total_trips
lo, hi = 1, min(time) * total_trips # upper bound
while lo < hi:
mid = (lo + hi) // 2
if can_complete(mid):
hi = mid
else:
lo = mid + 1
return lo
print(minimum_time([1, 2, 3], 5)) # 3 (3/1=3 + 3/2=1 + 3/3=1 = 5)
print(minimum_time([2], 1)) # 2Randfälle und Überprüfung
Wichtige Randfälle für beide Probleme: Task Scheduler — bei Cooldown n=0 lautet die Antwort einfach len(tasks), da kein Leerlauf erforderlich ist. Wenn alle Aufgaben identisch sind (z. B. alle 'A'), werden die Leerlaufplätze genau gefüllt. Wenn es viele verschiedene Aufgabentypen gibt, kann die Zahl der Leerlaufplätze 0 sein, weil die Aufgaben alle Zeitslots füllen. Gas Station — wenn die Gesamtmenge an Benzin genau den Gesamtkosten entspricht, gibt es genau einen gültigen Startpunkt. Wenn eine einzelne Tankstelle genug Benzin für die gesamte Rundfahrt hat, ist diese Tankstelle die Antwort. Überprüfen Sie Ihr Greedy-Ergebnis immer anhand dieser Sonderfälle.
from collections import Counter
def least_interval(tasks, n):
if n == 0: return len(tasks) # no cooldown
cnt = Counter(tasks)
mf = max(cnt.values())
mc = sum(1 for c in cnt.values() if c == mf)
return max(len(tasks), (mf-1)*(n+1)+mc)
# Edge cases for task scheduler
print(least_interval(['A','A','A'], 2)) # 7: A _ _ A _ _ A
print(least_interval(['A','A','B','B'], 0)) # 4: no idle
print(least_interval(['A','B','C','D'], 3)) # 4: all diff, no idle needed
# Edge case for gas station
def gas_station(gas, cost):
if sum(gas) < sum(cost): return -1
tank = start = 0
for i,(g,c) in enumerate(zip(gas,cost)):
tank += g-c
if tank < 0: tank=0; start=i+1
return start
print(gas_station([5,1,2,3,4],[4,4,1,5,1])) # 4Greedy-Muster erkennen
Sowohl Task Scheduler als auch Gas Station folgen demselben Greedy-Muster: (1) Identifizieren Sie den Engpass (die häufigste Aufgabe / die Nettokraftstoffbilanz). (2) Treffen Sie in einem einzigen Durchlauf eine Entscheidung mithilfe einer laufenden Variablen (max_freq, tank). (3) Beginnen Sie erneut oder setzen Sie zurück, sobald eine Einschränkung verletzt wird. Häufige Greedy-Probleme, die Sie kennen sollten, sind: Activity Selection, Huffman Coding, Fractional Knapsack, Jump Game, Task Scheduler, Gas Station und Merge Intervals. Für jedes dieser Probleme gibt es einen Beweis durch ein Austauschargument oder eine mathematische Invariante.
# Greedy pattern summary
# Task Scheduler:
# Bottleneck: max frequency task
# Formula: max(total_tasks, (max_freq-1)*(n+1)+max_count)
# O(n) time, O(1) space
# Gas Station:
# Bottleneck: running sum of (gas-cost) going negative
# Reset start when tank < 0, valid if total sum >= 0
# O(n) time, O(1) space
# Both avoid the need for DP by using a clever single-pass insight
from collections import Counter
def combined_demo(tasks, n, gas, cost):
ti = max(len(tasks), (max(Counter(tasks).values())-1)*(n+1) +
sum(1 for c in Counter(tasks).values() if c==max(Counter(tasks).values())))
tank = start = 0
gs = sum(g-c for g,c in zip(gas,cost)) >= 0
return ti, start if gs else -1Schnelltest
Testen Sie Ihr Verständnis der Konzepte aus Data Structures & Algorithms — Coding Interview Prep, die in dieser Lektion behandelt wurden.
Zusammenfassung der Lektion
In dieser Lektion haben Sie Folgendes gelernt: Antwort für Task Scheduler = max(total_tasks, (max_freq-1)*(n+1)+max_count) — hergeleitet durch das Füllen eines rasterartigen Zeitschemas mit der häufigsten Aufgabe, Gas Station verwendet einen einzigen Durchlauf und setzt start=i+1 zurück, sobald tank negativ wird; eine Lösung ist gültig, wenn die Gesamtmenge an Benzin ≥ den Gesamtkosten ist, und beide Probleme benötigen O(n) Zeit und O(1) Speicherplatz, da sie statt einer vollständigen Suche eine mathematische Invariante bestimmen. Als Nächstes untersuchen wir das Divide-and-Conquer-Schema und seine Anwendungen über Mergesort hinaus.
Häufig gestellte Fragen
Ist die Lektion „Task Scheduler und Gas Station“ kostenlos?
Ja — der vollständige Text von „Task Scheduler und Gas Station“ ist hier im Web kostenlos zu lesen. Um sie interaktiv zu üben (integrierter Code-Editor und 24/7 KI-Tutor) und den Rest des Coding Interview Prep-Kurses freizuschalten, upgrade auf CoddyKit PRO. Der Coding Interview Prep-Kurs umfasst insgesamt 4 Lektionen.
Was lerne ich in „Task Scheduler und Gas Station“?
Wenden Sie Greedy-Überlegungen auf das Problem der Abkühlzeit im CPU-Task-Scheduler und auf das Problem der Machbarkeit einer Rundfahrt durch Tankstellen an. Du übst Coding Interview Prep mit praktischem Code, den du direkt im Browser ausführst, und ein 24/7 KI-Tutor beantwortet deine Fragen während du die Lektion bearbeitest.
Brauche ich Erfahrung, um Coding Interview Prep zu starten?
Keine Vorkenntnisse erforderlich. Coding Interview Prep auf CoddyKit ist für Anfänger bis fortgeschrittene Lernende strukturiert, sodass du hier starten oder von Anfang an beginnen und in deinem eigenen Tempo voranschreiten kannst. Dies ist Lektion 4 von 4.
Wie lange dauert die Lektion „Task Scheduler und Gas Station“?
Die meisten CoddyKit-Lektionen dauern etwa 5–10 Minuten. Jede ist kompakt und interaktiv, sodass du stetig Fortschritte machst und genau dort weitermachst, wo du aufgehört hast – im Web und in der App.
Kann ich in dieser Coding Interview Prep-Lektion Code schreiben und ausführen?
Ja. Jede Coding Interview Prep-Lektion enthält einen integrierten Code-Editor, sodass du echten Code direkt in deinem Browser schreibst und ausführst und sofort KI-Feedback erhältst — ohne lokale Einrichtung erforderlich.
Alle Lektionen in diesem Kurs
- Greedy vs. dynamische Programmierung: Wann wird was verwendet?
- Intervallplanung und Zusammenführen
- Jump Game I und II
- Task Scheduler und Gas Station