Förberedelse inför kodningsintervjuer · Lektion

Intervallschemaläggning och sammanslagning

Lös meeting-rooms och non-overlapping-intervals genom att sortera efter sluttid, och merge-intervals genom att sortera efter starttid.

Lektion 2 av 413 steg

Intervallschemaläggning och sammanslagning är en gratis lektion i Förberedelse inför kodningsintervjuer på CoddyKit. Detta är lektion 2 av 4. Ni kan läsa hela lektionen gratis nedan och sedan öva praktiskt i webbläsaren med en inbyggd kodredigerare och en AI-handledare som är tillgänglig dygnet runt. Den ingår i lärvägen för Förberedelse inför kodningsintervjuer, och Era framsteg synkroniseras mellan webben och CoddyKit-appen. Kursen i Förberedelse inför kodningsintervjuer innehåller totalt 4 lektioner.

Översikt över intervallproblem

Intervallproblem dyker ständigt upp i intervjuer om schemaläggning, kalenderhantering och resursallokering. De viktigaste mönstren är: slå ihop överlappande intervall, räkna minsta antal mötesrum, hitta största mängd icke-överlappande intervall och lägga in ett nytt intervall. De flesta intervallproblem börjar med samma steg: sortera intervallen efter starttid (eller sluttid, beroende på problemet). Att välja rätt sorteringsnyckel är ofta den svåraste delen.

# Intervals: each = [start, end] (inclusive or exclusive by problem)
# Example:
intervals = [[1,3],[2,6],[8,10],[15,18]]
# Sorted by start (already sorted here)
# Visually:
# [1,3]    |-|
# [2,6]      |---|
# [8,10]             |--|
# [15,18]                    |---|
print('Intervals ready for analysis')

Slå ihop överlappande intervall

Merge Intervals (LeetCode 56): givet en lista med intervall ska alla överlappande intervall slås ihop. Algoritm: sortera efter starttid. Gå igenom den sorterade listan; om det aktuella intervallet överlappar det senast sammanslagna intervallet (dess start ≤ slutet för det senast sammanslagna intervallet), utökar ni det senast sammanslagna intervallets slut till det största av de två sluten. Annars lägger ni till det aktuella intervallet som ett nytt sammanslaget intervall. Tid: O(n log n) för sorteringen och O(n) för sammanslagningen.

def merge_intervals(intervals):
    intervals.sort(key=lambda x: x[0])  # sort by start
    merged = [intervals[0]]
    for start, end in intervals[1:]:
        last_end = merged[-1][1]
        if start <= last_end:
            # Overlapping: extend the last interval
            merged[-1][1] = max(last_end, end)
        else:
            # Non-overlapping: add as new interval
            merged.append([start, end])
    return merged

print(merge_intervals([[1,3],[2,6],[8,10],[15,18]]))
# [[1,6],[8,10],[15,18]]
print(merge_intervals([[1,4],[4,5]]))
# [[1,5]] (touching intervals merge)

Lägg in intervall

Insert Interval (LeetCode 57): givet en sorterad lista utan överlappningar ska ett nytt intervall läggas in och intervallen slås ihop igen. Gå igenom listan i tre faser: (1) Lägg till alla intervall som slutar innan det nya intervallet börjar. (2) Slå ihop alla intervall som överlappar det nya intervallet (utvidga dess gränser). (3) Lägg till alla återstående intervall. Detta är ett enda genomlopp i O(n) efter sorteringen i O(n log n) (som redan är gjord i det här problemet).

def insert_interval(intervals, new_interval):
    result = []
    i = 0
    n = len(intervals)
    # Phase 1: intervals before new_interval
    while i < n and intervals[i][1] < new_interval[0]:
        result.append(intervals[i])
        i += 1
    # Phase 2: merge overlapping intervals
    while i < n and intervals[i][0] <= new_interval[1]:
        new_interval[0] = min(new_interval[0], intervals[i][0])
        new_interval[1] = max(new_interval[1], intervals[i][1])
        i += 1
    result.append(new_interval)
    # Phase 3: remaining intervals
    while i < n:
        result.append(intervals[i])
        i += 1
    return result

print(insert_interval([[1,3],[6,9]], [2,5]))  # [[1,5],[6,9]]
print(insert_interval([[1,2],[3,5],[6,7],[8,10],[12,16]], [4,8]))
# [[1,2],[3,10],[12,16]]

Mötesrum I: Kan alla möten besökas

Meeting Rooms I (LeetCode 252): givet intervall för mötestider ska ni avgöra om en person kan delta i alla möten. Sortera efter starttid; om något möte börjar innan det föregående slutar överlappar mötena. Detta är den enklaste intervallkontrollen – totalt O(n log n). Den viktiga insikten är att ni efter sorteringen bara behöver jämföra intilliggande par.

def can_attend_meetings(intervals):
    intervals.sort(key=lambda x: x[0])
    for i in range(1, len(intervals)):
        # Current meeting starts before previous ends?
        if intervals[i][0] < intervals[i-1][1]:
            return False
    return True

print(can_attend_meetings([[0,30],[5,10],[15,20]]))  # False (0,30 overlaps 5,10)
print(can_attend_meetings([[7,10],[2,4]]))           # True (4 < 7, no overlap)

Mötesrum II: Minsta antal rum

Meeting Rooms II (LeetCode 253): hitta det minsta antalet konferensrum som krävs för att hålla alla möten samtidigt. Använd en min-heap för att hålla reda på rummet som slutar tidigast. Sortera mötena efter starttid. För varje nytt möte: om det börjar efter sluttiden för rummet som slutar tidigast återanvänder ni det rummet (ta bort och lägg till igen). Annars öppnar ni ett nytt rum. Heapens storlek i slutet är lika med antalet rum som behövs.

import heapq

def min_meeting_rooms(intervals):
    if not intervals: return 0
    intervals.sort(key=lambda x: x[0])  # sort by start
    heap = []  # min-heap of end times
    for start, end in intervals:
        if heap and heap[0] <= start:
            heapq.heapreplace(heap, end)  # reuse earliest-ending room
        else:
            heapq.heappush(heap, end)     # open a new room
    return len(heap)

print(min_meeting_rooms([[0,30],[5,10],[15,20]]))  # 2
print(min_meeting_rooms([[7,10],[2,4]]))           # 1
print(min_meeting_rooms([[9,10],[4,9],[4,17]]))    # 2

Alternativ med sveplinje för rumsräkning

Ett alternativ i O(n log n): sveplinje. Skapa händelser för varje intervalls start (+1) och slut (-1). Sortera alla händelser efter tid (vid lika tider: slut före start om ni vill ha icke-inkluderande intervall). Svep från vänster till höger och håll reda på ett löpande antal aktiva möten. Det största antalet är det minsta antalet rum som behövs. Detta är mer intuitivt för vissa och kan generaliseras till andra räkneproblem för intervall.

def min_rooms_sweep(intervals):
    events = []
    for start, end in intervals:
        events.append((start, 1))   # meeting starts
        events.append((end, -1))    # meeting ends
    # Sort: same time → end (-1) before start (1) if exclusive
    events.sort(key=lambda x: (x[0], x[1]))
    max_rooms = current = 0
    for _, delta in events:
        current += delta
        max_rooms = max(max_rooms, current)
    return max_rooms

print(min_rooms_sweep([[0,30],[5,10],[15,20]]))  # 2
print(min_rooms_sweep([[1,5],[2,6],[3,7]]))       # 3 (all overlap at t=3)

Icke-överlappande intervall: maximalt urval

Non-Overlapping Intervals (LeetCode 435): hitta det minsta antalet intervall som måste tas bort för att de återstående inte ska överlappa. Detta motsvarar att hitta det största antalet icke-överlappande intervall (aktivitetsurval) och returnera de återstående som borttagna. Sortera efter sluttid: behåll med greedy-strategin det intervall som slutar tidigast (det ger mest utrymme för framtida intervall). När nästa intervall överlappar förkastar ni det (räkna en borttagning).

def erase_overlap_intervals(intervals):
    if not intervals: return 0
    intervals.sort(key=lambda x: x[1])  # sort by END time
    removals = 0
    last_end = float('-inf')
    for start, end in intervals:
        if start >= last_end:
            last_end = end  # keep this interval
        else:
            removals += 1   # remove this interval (it overlaps)
    return removals

print(erase_overlap_intervals([[1,2],[2,3],[3,4],[1,3]]))  # 1 (remove [1,3])
print(erase_overlap_intervals([[1,2],[1,2],[1,2]]))        # 2
print(erase_overlap_intervals([[1,2],[2,3]]))              # 0 (no overlap)

Varför sortera efter sluttid, inte starttid?

För aktivitetsurval (största möjliga mängd aktiviteter utan överlappning) är det bevisat optimalt att sortera efter sluttid. Intuitionen är att en aktivitet som slutar tidigt lämnar mer utrymme för framtida aktiviteter. Om vi sorterar efter starttid kan vi välja en mycket lång aktivitet som börjar tidigt och blockerar många kortare aktiviteter som börjar senare. Bytesargument: om den optimala lösningen väljer aktivitet A framför den aktivitet G som slutar tidigast, byt ut A mot G — G slutar inte senare och står därför inte i konflikt med något som A inte stod i konflikt med.

# Counterexample for sorting by START time:
# [[1,10],[2,3],[4,5]] — sorted by start: [1,10],[2,3],[4,5]
# Sort-by-start greedy keeps [1,10], can't add [2,3] or [4,5] (all overlap [1,10])
# Selects: 1 interval

# Sort-by-end greedy:
# [[2,3],[4,5],[1,10]] — sorted by end
# Keep [2,3] (end=3), then [4,5] (start=4 >= 3, keep), then [1,10] (start=1 < 5, skip)
# Selects: 2 intervals — OPTIMAL

intervals = [[1,10],[2,3],[4,5]]
intervals.sort(key=lambda x: x[1])
last_end = float('-inf')
count = 0
for s, e in intervals:
    if s >= last_end:
        count += 1; last_end = e
print('Max non-overlapping:', count)  # 2

Skärningar mellan intervallistor

Skärningar mellan intervallistor (LeetCode 986): hitta alla par som skär varandra från två sorterade intervallistor. Använd en tvåpekaralgoritm. Beräkna skärningen mellan det aktuella paret i varje steg (det största av startpunkterna och det minsta av slutpunkterna). Om start ≤ slut är skärningen giltig. Flytta sedan pekaren för det intervall som slutar först. Tidskomplexiteten är O(m+n).

def interval_intersection(A, B):
    result = []
    i = j = 0
    while i < len(A) and j < len(B):
        # Intersection boundaries
        lo = max(A[i][0], B[j][0])
        hi = min(A[i][1], B[j][1])
        if lo <= hi:
            result.append([lo, hi])  # valid intersection
        # Advance pointer of interval that ends first
        if A[i][1] < B[j][1]:
            i += 1
        else:
            j += 1
    return result

A = [[0,2],[5,10],[13,23],[24,25]]
B = [[1,5],[8,12],[15,24],[25,26]]
print(interval_intersection(A, B))
# [[1,2],[5,5],[8,10],[15,23],[24,24],[25,25]]

Partitionering av tecken

Partitionering av tecken (LeetCode 763): dela upp en sträng i så många delar som möjligt, så att varje tecken förekommer i högst en del. Girig metod: hitta den sista förekomsten av varje tecken. Gå igenom strängen och håll reda på ett max_end. När i == max_end är den aktuella partitionen klar — registrera dess längd och börja på en ny partition. Detta är i själva verket ett intervallsammanslagningsproblem.

def partition_labels(s):
    last = {c: i for i, c in enumerate(s)}  # last occurrence of each char
    partitions = []
    start = max_end = 0
    for i, c in enumerate(s):
        max_end = max(max_end, last[c])
        if i == max_end:  # partition complete
            partitions.append(max_end - start + 1)
            start = i + 1
    return partitions

print(partition_labels('ababcbacadefegdehijhklij'))
# [9, 7, 8] — parts 'ababcbaca', 'defegde', 'hijhklij'

Sammanfattning av intervallproblem

Behärska dessa fyra mönster för intervall: (1) Sammanfoga: sortera efter start och förläng det sista intervallet om det överlappar. (2) Räkna rum: sortera efter start och använd en min-heap med sluttider. (3) Maximalt antal utan överlappning: sortera efter slut och välj girigt. (4) Infoga: en linjär genomsökning i tre faser. Sorteringsnyckeln är viktig: sammanfogning använder start, medan maximering använder slut. Tidskomplexiteten är alltid O(n log n), där sorteringen dominerar; sammanfogning och genomsökning är O(n).

# Quick reference:
# Merge intervals:         sort by start, extend if overlap
# Insert interval:         three-phase linear scan
# Meeting rooms (can?):   sort by start, check consecutive overlap
# Meeting rooms (min?):   sort by start, min-heap of end times / sweep
# Max non-overlapping:    sort by END, greedy keep
# Min removals:           n - max_non_overlapping
# Interval intersection:  two pointers on sorted lists

print('Pattern: sort key is the decisive choice')
print('Merge → sort by start')
print('Activity selection → sort by end')
print('Room count → sort by start + heap of ends')

Snabbtest

Testa era kunskaper om begreppen inom Data Structures & Algorithms — Coding Interview Prep från den här lektionen.

Sammanfattning av lektionen

I den här lektionen har ni lärt er: slå ihop intervall genom att sortera efter start och förlänga det sista intervallet om en överlappning uppstår, minsta antal mötesrum kräver sortering efter start plus en min-heap med sluttider, där rum återanvänds när rummet som slutar tidigast blir ledigt, samt maximalt antal intervall utan överlappning använder girigt urval efter sluttid. Nästa steg är Jump Game I och II — problem om nåbarhet och minsta antal hopp som löses genom girig räckviddsutvidgning.

Gratis att börja

Lär dig Förberedelse inför kodningsintervjuer med en AI-lärare – gratis

Skriv och kör riktig kod i webbläsaren, få omedelbar hjälp av en AI-lärare dygnet runt och fortsätt där du slutade – på webben eller i appen.

Kurser
90
Lektioner
360

Vanliga frågor

Är lektionen ”Intervallschemaläggning och sammanslagning” gratis?

Ja – hela texten till ”Intervallschemaläggning och sammanslagning” kan läsas gratis här på webben. Om Ni vill öva interaktivt med en inbyggd kodredigerare och en AI-handledare som är tillgänglig dygnet runt och låsa upp resten av kursen i Förberedelse inför kodningsintervjuer, kan Ni uppgradera till CoddyKit PRO. Kursen i Förberedelse inför kodningsintervjuer innehåller totalt 4 lektioner.

Vad lär jag mig i ”Intervallschemaläggning och sammanslagning”?

Lös meeting-rooms och non-overlapping-intervals genom att sortera efter sluttid, och merge-intervals genom att sortera efter starttid. Ni övar på Förberedelse inför kodningsintervjuer med praktisk kod som körs direkt i webbläsaren, medan en AI-handledare som är tillgänglig dygnet runt svarar på Era frågor under lektionen.

Behöver jag någon erfarenhet för att börja lära mig Förberedelse inför kodningsintervjuer?

Du behöver inga förkunskaper. Utbildningen i Förberedelse inför kodningsintervjuer på CoddyKit är upplagd för allt från nybörjare till avancerade elever, så att du kan börja här eller från början och gå fram i din egen takt. Detta är lektion 2 av 4.

Hur lång tid tar lektionen ”Intervallschemaläggning och sammanslagning”?

De flesta CoddyKit-lektioner tar cirka 5–10 minuter. Varje lektion är kort och interaktiv, så att du gör stadiga framsteg och kan fortsätta precis där du slutade – på webben eller i appen.

Kan jag skriva och köra kod i den här Förberedelse inför kodningsintervjuer-lektionen?

Ja. Varje Förberedelse inför kodningsintervjuer-lektion innehåller en inbyggd kodredigerare, så att du kan skriva och köra riktig kod direkt i webbläsaren och få omedelbar AI-feedback – utan lokal installation.

Alla lektioner i den här kursen

  1. Giriga algoritmer eller DP: när används vad
  2. Intervallschemaläggning och sammanslagning
  3. Jump Game I och II
  4. Task Scheduler och Gas Station
← Tillbaka till Förberedelse inför kodningsintervjuer