Warum verwenden wir die schnelle Sortierung nicht für eine verknüpfte Liste?


16

Der schnelle Sortieralgorithmus kann in die folgenden Schritte unterteilt werden

  1. Pivot identifizieren.

  2. Partitionieren Sie die verknüpfte Liste basierend auf Pivot.

  3. Teilen Sie die verknüpfte Liste rekursiv in zwei Teile.

Wenn ich immer das letzte Element als Pivot wähle, dauert die Identifizierung des Pivot-Elements (1. Schritt) Mal.O(n)

Nachdem wir das Pivot-Element identifiziert haben, können wir seine Daten speichern und mit allen anderen Elementen vergleichen, um den richtigen Partitionspunkt zu ermitteln (2. Schritt). Jeder Vergleich benötigt Zeit, da wir die Pivot-Daten speichern, und jeder Swap benötigt O ( 1 ) Zeit. Insgesamt dauert es also O ( n ) Zeit für n Elemente.O(1)O(1)O(n)n

Die Wiederholungsbeziehung lautet also:

ist O ( n log n ) und entspricht der Sortierung beim Zusammenführen mit einer verknüpften Liste.T(n)=2T(n/2)+nO(nlogn)

Warum wird die Sortierung beim Zusammenführen für verknüpfte Listen der schnellen Sortierung vorgezogen?


Es ist nicht erforderlich, das letzte Element als Drehpunkt anstelle des ersten
auszuwählen

Antworten:


19

Das Speicherzugriffsmuster in Quicksort ist zufällig, und die sofort einsatzbereite Implementierung ist vorhanden. Daher werden viele Auslagerungsversuche durchgeführt, um ein geordnetes Ergebnis zu erzielen.
Während die Zusammenführungssortierung extern ist, ist ein zusätzliches Array erforderlich, um das geordnete Ergebnis zurückzugeben. In Arrays bedeutet dies zusätzlichen Speicherplatzaufwand. In dem Fall, dass eine verknüpfte Liste vorhanden ist, kann der Wert herausgezogen und das Zusammenführen von Knoten gestartet werden. Der Zugriff erfolgt sequentieller.

Aus diesem Grund ist die Quicksortierung für verknüpfte Listen keine natürliche Wahl, während das Sortieren durch Zusammenführen große Vorteile bringt.

Die Landau-Notation könnte (mehr oder weniger, weil Quicksort immer noch ) zustimmen, aber die Konstante ist viel höher.O(n2)

Im Durchschnitt sind beide Algorithmen in so dass der asymptotische Fall derselbe ist, aber die Präferenz liegt ausschließlich an der versteckten Konstante und manchmal ist die Stabilität das Problem (Quicksort ist von Natur aus instabil, Mergsort ist stabil).O(nlogn)


Aber die durchschnittliche zeitliche Komplexität ist gleich oder? Verwenden Sie sowohl die schnelle Sortierung als auch die Sortierung für verknüpfte Listen.
Zephyr

10
@ Zephyr, Sie müssen sich daran erinnern, dass die Komplexitätsnotation konstante Faktoren fallen lässt. Ja, Quicksortieren in einer verknüpften Liste und Zusammenführen in einer verknüpften Liste sind dieselbe Komplexitätsklasse, aber die Konstanten, die Sie nicht sehen, beschleunigen das Zusammenführen gleichmäßig.
Mark

@ Zephyr Grundsätzlich ist es der Unterschied zwischen theoretischen und empirischen Ergebnissen. Empirisch ist Quicksort schneller.
Ferit

1
O(n2)

3
O(logn)

5

O(n)O(n2)

O(1)

264O(1)

head = list.head;
head_array = array of 64 nulls

while head is not null
    current = head;
    head = head.next;
    current.next = null;
    for(i from 0 to 64)
        if head_array[i] is null
            head_array[i] = current;
            break from for loop;
        end if
        current = merge_lists(current, array[i]);
        head_array[i] = null;
     end for
end while

current = null;
for(i from 0 to 64)
    if head_array[i] is not null
        if current is not null
            current = merge_lists(current, head_array[i]);
        else
            current = head_array[i];
        end if
     end if
 end for

 list.head = current;

Dies ist der Algorithmus, den der Linux-Kernel zum Sortieren seiner verknüpften Listen verwendet. Allerdings mit einigen zusätzlichen Optimierungen wie dem Ignorieren des previousZeigers während aller bis auf den letzten Zusammenführungsvorgang.


-2

Sie können Merge-Sortierung, Partition-Sortierung, Baum-Sortierung und Vergleichsergebnisse
schreiben. Es ist ziemlich einfach, Baum-Sortierung zu schreiben, wenn Sie etwas zusätzlichen Platz zulassen.
Für Baum-Sortierung muss jeder Knoten der verknüpften Liste zwei Zeiger haben, auch wenn wir einfach verknüpfte Liste
in verknüpfter Liste sortieren Ich bevorzuge das Einfügen und Löschen, anstatt die
Hoare-Partition auszutauschen. Dies ist nur für doppelt verknüpfte Listen möglich

program untitled;


type TData = longint;
     PNode = ^TNode;
     TNode = record
                data:TData;
                prev:PNode;
                next:PNode;
             end;

procedure ListInit(var head:PNode);
begin
  head := NIL;
end;

function ListIsEmpty(head:PNode):boolean;
begin
  ListIsEmpty := head = NIL;
end;

function ListSearch(var head:PNode;k:TData):PNode;
var x:PNode;
begin
  x := head;
  while (x <> NIL)and(x^.data <> k)do
     x := x^.next;
  ListSearch := x;
end;

procedure ListInsert(var head:PNode;k:TData);
var x:PNode;
begin
  new(x);
  x^.data := k;
  x^.next := head;
  if head <> NIL then
     head^.prev := x;
   head := x;
   x^.prev := NIL;
end;

procedure ListDelete(var head:PNode;k:TData);
var x:PNode;
begin
   x := ListSearch(head,k);
   if x <> NIL then
   begin
     if x^.prev <> NIL then
        x^.prev^.next := x^.next
      else 
        head := x^.next;
     if x^.next <> NIL then
        x^.next^.prev := x^.prev;
     dispose(x);
   end;
end;

procedure ListPrint(head:PNode);
var x:PNode;
    counter:longint;
begin
  x := head;
  counter := 0;
  while x <> NIL do
  begin
    write(x^.data,' -> ');
    x := x^.next;
    counter := counter + 1;
  end;
  writeln('NIL');
  writeln('Liczba elementow listy : ',counter);
end;

procedure BSTinsert(x:PNode;var t:PNode);
begin
  if t = NIL then
    t := x
  else
    if t^.data = x^.data then
            BSTinsert(x,t^.prev)
        else if t^.data < x^.data then
            BSTinsert(x,t^.next)
        else
            BSTinsert(x,t^.prev);
end;

procedure BSTtoDLL(t:PNode;var L:PNode);
begin
   if t <> NIL then
   begin
     BSTtoDLL(t^.next,L);
     ListInsert(L,t^.data);
     BSTtoDLL(t^.prev,L);
   end;
end;

procedure BSTdispose(t:PNode);
begin
   if t <> NIL then
   begin
    BSTdispose(t^.prev);
    BSTdispose(t^.next);
    dispose(t);
   end; 
end;

procedure BSTsort(var L:PNode);
var T,S:PNode;
    x,xs:PNode;
begin
  T := NIL;
  S := NIL;
  x := L;
  while x <> NIL do
  begin
    xs := x^.next;
    x^.prev := NIL;
    x^.next := NIL;
    BSTinsert(x,t);
    x := xs;
  end;
  BSTtoDLL(T,S);
  BSTdispose(T);
  L := S;
end;

var i : byte;
    head:PNode;
    k:TData;
BEGIN
  ListInit(head);
  repeat
     writeln('0. Wyjscie');
     writeln('1. Wstaw element na poczatek listy');
     writeln('2. Usun element listy');
     writeln('3. Posortuj elementy drzewem binarnym');
     writeln('4. Wypisz elementy  listy');
     readln(i);
     case i of
     0:
     begin
       while not ListIsEmpty(head) do
            ListDelete(head,head^.data);
     end;
     1:
     begin
       writeln('Podaj element jaki chcesz wstawic');
       readln(k);
       ListInsert(head,k);
     end;
     2:
     begin
       writeln('Podaj element jaki chcesz usunac');
       readln(k);
       ListDelete(head,k);
     end;
     3:
     begin
       BSTsort(head);
     end;
     4:
     begin
        ListPrint(head);    
     end
     else
        writeln('Brak operacji podaj inny numer');
     end;
  until i = 0;  
END.

Dieser Code muss verbessert werden.
Zuerst sollten wir den zusätzlichen Speicher auf die Rekursionsanforderungen beschränken,
dann sollten wir versuchen, die Rekursion durch eine Iteration zu ersetzen.
Wenn wir den Algorithmus weiter verbessern möchten, sollten wir den Self Balancing Tree verwenden


Vielen Dank für Ihren ausführlichen Beitrag, aber dies ist keine Codierungsseite. 200 Codezeilen erklären nicht, warum die Zusammenführungssortierung für verknüpfte Listen der schnellen Sortierung vorgezogen wird.
David Richerby

In der Partitionssortierung ist die Auswahl des Pivots auf das erste oder letzte Element beschränkt (letztes, wenn der Zeiger auf den Endknoten gezeigt wird). Andernfalls ist die Auswahl des Pivots langsam. Die Hoare-Partitionierung ist nur für doppelt verknüpfte Listen möglich Baum hat die gleiche Kompexität wie Quicksort, wenn wir konstanten Faktor ignorieren, aber es ist einfacher, den schlimmsten Fall bei der Baumsortierung zu vermeiden. Bei der Zusammenführungssortierung sind zu wenige Zeichen im Kommentar enthalten
Mariusz

-2

Quicksort
Vielleicht zeige ich Schritte für Quicksort

Wenn die Liste mehr als einen Knoten enthält

  1. Drehpunktauswahl
  2. Partitionsliste in drei Unterlisten Die
    erste Unterliste enthält Knoten mit Schlüsseln, die kleiner als der Pivot-Schlüssel sind. Die
    zweite Unterliste enthält Knoten mit Schlüsseln, die gleich dem Pivot-Schlüssel sind. Die
    dritte Unterliste enthält Knoten mit Schlüsseln, die größer als der Pivot-Schlüssel sind
  3. Rekursive Aufrufe für Unterlisten, deren Knoten ungleich Pivot-Knoten sind
  4. Verketten Sie sortierte Unterlisten zu einer sortierten Liste

Zu 1.
Wenn wir Pivot schnell auswählen möchten, ist die Auswahl begrenzt.
Wir können
Kopfknoten oder Endknoten auswählen. Unsere Liste muss auf den Knoten ausgerichtet sein, wenn unser Pivot
schnell verfügbar sein soll. Andernfalls müssen wir nach Knoten suchen

Zu 2.
Wir können Warteschlangenoperationen für diesen Schritt verwenden.
Zuerst entfernen wir den Knoten aus der ursprünglichen verknüpften Liste und
vergleichen seinen Schlüssel mit dem Pivot-Schlüssel und stellen den Knoten in die richtige Unterliste.
Unterlisten werden aus vorhandenen Knoten erstellt und es ist nicht erforderlich,
Speicher für neue Knoten zuzuweisen

Der Zeiger auf den Endknoten ist nützlich, da Warteschlangenoperationen
und Verkettungen bei Vorhandensein dieses Zeigers schneller ausgeführt werden


Ich fürchte, ich konnte nicht sehen, wie dies die Frage beantwortet.
Apass.Jack
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.