Task Scheduler og Gas Station
Anvend grådig problemløsning på problemet med CPU-opgaver og afkølingsperioder samt problemet om gennemførlighed ved en cirkulær tankstation.
Task Scheduler og Gas Station er en gratis Forberedelse til kodeinterviews-lektion på CoddyKit. Dette er lektion 4 af 4. Du kan læse hele lektionen gratis nedenfor — og derefter øve dig praktisk i browseren med en indbygget kodeeditor og en AI-vejleder, der er tilgængelig døgnet rundt. Den er en del af læringsforløbet i Forberedelse til kodeinterviews, og dine fremskridt synkroniseres på tværs af nettet og CoddyKit-appen. Forberedelse til kodeinterviews-kurset indeholder 4 lektioner i alt.
Task Scheduler-problemet
Task Scheduler (LeetCode 621): Du får en liste over CPU-opgaver (hver mærket A-Z) og en afkølingsperiode n. Find det mindste antal CPU-intervaller, der kræves for at udføre alle opgaver. Den samme opgave skal vente mindst n intervaller, før den kan køres igen. Ledige intervaller er tilladt. For opgaverne ['A','A','A','B','B','B'] med en afkølingsperiode på 2 er svaret 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')Grådig formel til Task Scheduler
Den vigtigste pointe er, at den samlede tid bestemmes af den opgave, der forekommer oftest. Hvis den hyppigste opgave forekommer f gange, og der er max_count opgaver med frekvens f, er tiden max(len(tasks), (f-1) * (n+1) + max_count). Formlen opretter f-1 blokke af størrelsen n+1, fylder dem med andre opgaver og tilføjer den sidste cyklus. Hvis andre opgaver udfylder alle ledige pladser (mange forskellige opgaver), udføres alle opgaver blot uden ventetid.
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)) # 10Hvorfor formlen virker
Visualisér tidsplanen som et gitter med n+1 kolonner (én opgaveplads og n afkølingspladser). Den hyppigste opgave A (med frekvens f) kræver f rækker. Mellem den første og sidste forekomst er der f-1 komplette blokke med n+1 pladser. Dertil kommer den sidste delvise blok, som indeholder alle opgaver med den maksimale frekvens. Hvis der er tilstrækkeligt mange forskellige opgaver, fylder de alle ledige pladser, og det faktiske opgaveantal overstiger blokkens tidsforbrug — vælg det største af de to.
# 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)}')Alternativ: simulering med heap
En heap-baseret simulering giver den faktiske tidsplan, ikke kun antallet. Ved hvert trin vælges den hyppigste tilgængelige opgave fra en max-heap. Efter udførelsen anvendes afkøling: Opgaven må først indsættes igen n trin senere. Brug en kø til at holde styr på opgaver, der afkøles. Dette bruger O(total_time × log k) tid, hvor k er antallet af forskellige opgaver. Selvom metoden er korrekt, er formlen hurtigere. Kend begge metoder — interviewere kan bede om selve tidsplanen.
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-problemet
Gas Station (LeetCode 134): Der er n tankstationer i en cirkel. Station i har gas[i] brændstof, og det koster cost[i] at køre til den næste station. Start med en tom tank, og find den station, hvorfra du kan gennemføre rundturen. Hvis der ikke findes en sådan station, returnér -1. Problemet garanterer, at der højst findes én gyldig løsning, hvis der findes en.
# 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 existsGrådig løsning til Gas Station
Grådig algoritme: (1) Hvis den samlede mængde brændstof er mindre end de samlede omkostninger, findes der ingen løsning (returnér -1). (2) Ellers findes der præcis én løsning. Find den med ét gennemløb: Følg tank (det aktuelle brændstof) og start (kandidaten til startstation). Hvis tank < 0 efter et besøg på en station, kan det aktuelle start ikke nå denne station — nulstil tank = 0, og sæt start = i + 1. Det endelige start er svaret.
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)) # -1Hvorfor den grådige start er korrekt
Korrekthedsargument: Hvis tanken bliver negativ, efter at du fra start har nået station i, kan ingen station mellem start og i (inklusive begge) være et gyldigt startpunkt — de har alle mindre brændstof, når de når station i, end en start fra start ville have. Derfor kan vi trygt springe dem alle over og prøve i+1. Da der findes en løsning (den samlede mængde brændstof ≥ de samlede omkostninger), skal den endelige kandidat start virke.
# 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)) # TrueUdtømmende søgning kontra grådig metode til Gas Station
Udtømmende søgning prøver hver startstation og simulerer hele rundturen — O(n²) tid. Den grådige løsning med ét gennemløb bruger O(n) tid og O(1) plads. For et array med 10⁵ stationer er forskellen 10¹⁰ operationer mod 10⁵. Den afgørende matematiske egenskab, der gør den grådige metode mulig, er: Hvis den samlede nettomængde brændstof er ikke-negativ, findes der et gyldigt startpunkt, og det er altid stationen lige efter det sidste punkt, hvor den løbende sum blev negativ.
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))Relateret: Minimumsomkostning for at gennemføre ture
Minimumstid for at gennemføre ture (LeetCode 2187) er et problem med binær søgning i svarrummet. Du laver binær søgning på tidsværdien T: For en given tid T gennemfører busser med time[i] floor(T/time[i]) ture. Hvis det samlede antal ture ≥ totalTrips, er T tilstrækkelig. Find den mindste værdi af T. Dette viser, at grådighed kan anvendes på metaniveauet (ved at lave binær søgning blandt svar), når der ikke findes en direkte grådig regel på objektniveauet.
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)) # 2Grænsetilfælde og verifikation
Vigtige grænsetilfælde for begge problemer: Opgaveplanlæggeren — når nedkøling n=0, er svaret ganske enkelt len(tasks) (der er ikke brug for tomgang). Når alle opgaver er ens (f.eks. alle 'A'), udfyldes de ledige pladser præcist. Når opgaverne har mange forskellige typer, kan antallet af ledige pladser være 0 (opgaverne udfylder alle rammer). Benzinstationen — når den samlede mængde brændstof er nøjagtigt lig med de samlede omkostninger, findes der præcis én gyldig start. Når en enkelt station har nok brændstof til hele ruten, er denne station svaret. Kontrollér altid dit grådige svar på disse degenererede tilfælde.
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])) # 4Genkendelse af grådige mønstre
Både Opgaveplanlæggeren og Benzinstationen følger det grådige mønster: (1) Identificér flaskehalsen (den hyppigst forekommende opgave / nettobalancen for brændstof). (2) Træf en beslutning i én gennemkørsel med en løbende variabel (max_freq, tank). (3) Start forfra eller nulstil, når en begrænsning overtrædes. Almindelige grådige problemer, du bør kende: aktivitetsudvælgelse, Huffman-kodning, fraktioneret rygsæk, spring-spil, Opgaveplanlæggeren, Benzinstationen og fletning af intervaller. Hvert af dem har et bevis via et ombytningsargument eller en matematisk invariant.
# 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 -1Hurtig test
Test din forståelse af begreberne fra lektionen i Data Structures & Algorithms — Coding Interview Prep.
Opsummering af lektionen
I denne lektion lærte du: Svar for Opgaveplanlæggeren = max(total_tasks, (max_freq-1)*(n+1)+max_count) — udledt ved at udfylde gittere baseret på rammer med den hyppigst forekommende opgave, Benzinstationen bruger én gennemkørsel og nulstiller start=i+1, hver gang tank går under nul; løsningen er gyldig, når den samlede mængde brændstof ≥ de samlede omkostninger, og begge problemer bruger O(n)-tid og O(1)-plads ved at identificere en matematisk invariant i stedet for at søge udtømmende. Som det næste ser vi på skabelonen del og hersk og dens anvendelser ud over fletningssortering.
Lær Forberedelse til kodeinterviews med en AI-underviser — gratis
Skriv og kør rigtig kode i din browser, få øjeblikkelig hjælp fra en AI-underviser døgnet rundt, og fortsæt, hvor du slap, på web eller i appen.
- Kurser
- 90
- Lektioner
- 360
Ofte stillede spørgsmål
Er lektionen “Task Scheduler og Gas Station” gratis?
Ja — hele teksten til “Task Scheduler og Gas Station” kan læses gratis her på nettet. Hvis du vil øve dig interaktivt med en indbygget kodeeditor og en AI-vejleder døgnet rundt og få adgang til resten af Forberedelse til kodeinterviews-kurset, skal du opgradere til CoddyKit PRO. Forberedelse til kodeinterviews-kurset indeholder 4 lektioner i alt.
Hvad lærer jeg i “Task Scheduler og Gas Station”?
Anvend grådig problemløsning på problemet med CPU-opgaver og afkølingsperioder samt problemet om gennemførlighed ved en cirkulær tankstation. Du øver dig i Forberedelse til kodeinterviews med praktisk kode, som du kører direkte i browseren, og en AI-vejleder døgnet rundt besvarer dine spørgsmål, mens du arbejder dig gennem lektionen.
Skal jeg have erfaring for at begynde på Forberedelse til kodeinterviews?
Der kræves ingen tidligere erfaring. Forberedelse til kodeinterviews på CoddyKit er tilrettelagt for både begyndere og øvede, så du kan starte her eller fra begyndelsen og lære i dit eget tempo. Dette er lektion 4 af 4.
Hvor lang tid tager lektionen “Task Scheduler og Gas Station”?
De fleste CoddyKit-lektioner tager cirka 5–10 minutter. Hver lektion er kort og interaktiv, så du gør løbende fremskridt og kan fortsætte, hvor du slap – på både web og app.
Kan jeg skrive og køre kode i denne Forberedelse til kodeinterviews-lektion?
Ja. Alle Forberedelse til kodeinterviews-lektioner har en indbygget kodeeditor, så du kan skrive og køre rigtig kode direkte i din browser og få øjeblikkelig feedback fra AI – uden lokal opsætning.
Alle lektioner i dette kursus
- Grådige algoritmer eller DP: Hvornår bruges hvad
- Intervalplanlægning og sammenfletning
- Jump Game I og II
- Task Scheduler og Gas Station