Hoppa till innehåll

Mergesortering

↑
Sortering Interaktiv genomgång

Mergesortering delar upp listan i mindre delar och slår sedan ihop dem i sorterad ordning. Tidsåtgången är förutsägbar även för stora listor.

Egenskap Kom ihåg
Vanlig tidskomplexitet O(n log n)
Extra minne O(n)
Stabil? Ja, med implementationen nedan
Passar bäst när Du vill ha förutsägbar prestanda även på stora listor

Staplarna visar värden och positioner. Välj Nästa för att undersöka ett moment eller Spela för hela förloppet. Testa egna tal, gärna en redan sorterad och en omvänt sorterad lista.

Interaktiv genomgång0 / 0
Förklara stegen själv och visa svaret när du vill.
Vad händer?

Beskriv själv vad algoritmen gör just nu.

Varför gör vi det?

Varför behövs det här steget?

Nästa steg

Försök förutse nästa steg.

Jämförelser 0Skrivningar 0Händelser 0

Lila: jämförs eller flyttas | Grönt: färdig del | Orange: aktuellt arbetsområde

Ett exempel på delning och sammanslagning

Section titled “Ett exempel på delning och sammanslagning”

Listan 7, 3, 5, 2 delas och slås ihop till 2, 3, 5, 7

Diagrammet visar hur listan först delas till enskilda element. Därefter sammanfogas de sorterade delarna från botten och upp.

  1. Hitta mitten. Dela intervallet i en vänster och en höger del.
  2. Sortera halvorna. Gör samma sak rekursivt tills varje intervall innehåller ett element.
  3. Använd en tillfällig buffert. Vid sammanslagningen är varje halva redan sorterad.
  4. Jämför de första återstående värdena. Lägg det mindre värdet i bufferten. Vid lika värden väljer vi vänster sida först för att behålla stabilitet.
  5. Kopiera resten. När ena halvan tar slut kan återstoden av den andra kopieras direkt.
  6. Kopiera tillbaka. Nu är hela intervallet sorterat och den rekursiva processen fortsätter.

Med [7, 3, 5] delar vi i [7] och [3, 5], och sedan i [3] och [5]. Slå ihop till [3, 5]. Jämför därefter första kvarvarande värdet på varje sida: 3, 5, 7. Resultatet blir [3, 5, 7].

C# | MergeSort.cs
static void MergeSort(int[] a)
{
int[] buffer = new int[a.Length];
Sort(0, a.Length);
void Sort(int start, int end)
{
if (end - start <= 1) return;
int middle = start + (end - start) / 2;
Sort(start, middle);
Sort(middle, end);
int left = start, right = middle;
for (int k = start; k < end; k++)
{
if (right == end ||
(left < middle && a[left] <= a[right]))
buffer[k] = a[left++];
else
buffer[k] = a[right++];
}
Array.Copy(buffer, start, a, start, end - start);
}
}
Mått Kostnad Förklaring
Bästa fall O(n log n) Delning och sammanslagning sker ändå.
Genomsnitt O(n log n) Logaritmiskt antal nivåer, linjärt arbete per nivå.
Sämsta fall O(n log n) Varje nivå behandlar alla n element.
Extra minne O(n) Implementationen använder en buffert.
Stabilitet Stabil Välj vänster halva vid lika värden.

Mergesortering håller en god övre gräns för tidsåtgången men använder en extra buffert. Metoden passar när du behöver stabil sortering och konsekvent prestanda.

  • Att försöka slå ihop delar innan de är sorterade.
  • Att blanda ihop inkluderande och exkluderande intervallgränser. Här används [start, end).
  • Att glömma värden som blir kvar när ena halvan är slut.
  • Att förbise buffertens minneskostnad.

Båda halvorna är redan sorterade när de ska slås ihop. Nästa värde måste därför vara det minsta av de två första kvarvarande värdena. När ett värde har kopierats behöver det inte flyttas igen i den sammanslagningen. När en halva tar slut kopieras resten av den andra.

En vanlig arraybaserad mergesort använder en extra buffert vars storlek växer med antalet objekt. Det är normalt bättre att återanvända en buffert än att skapa nya arrayer i varje rekursivt steg. Rekursionen behöver dessutom stackminne, men bufferten dominerar ofta minnesanalysen.

När båda framvärdena är lika ska vi välja det från vänster halva först. Då behåller lika objekt sin tidigare ordning. Det spelar roll om objekten redan sorterats efter ett annat fält.