Algorytm szybkiego sortowania można podzielić na następujące kroki
Zidentyfikuj oś przestawną.
Podziel listę połączoną na partycje na podstawie przestawnej.
Podziel listę połączoną rekurencyjnie na 2 części.
Teraz, jeśli zawsze wybieram ostatni element jako oś przestawną, wówczas identyfikacja elementu przestawnego (1. krok) zajmuje czas.
Po zidentyfikowaniu elementu przestawnego możemy przechowywać jego dane i porównać je ze wszystkimi innymi elementami, aby zidentyfikować prawidłowy punkt podziału (drugi krok). Każde porównanie zajmie ponieważ przechowujemy dane przestawne, a każda zamiana zajmuje . Tak więc w sumie zajmuje dla elementów.
Tak więc relacja powtarzalności jest następująca:
który jest który jest taki sam jak w sortowaniu scalonym z listą połączoną.
Dlaczego więc sortowanie według scalania jest lepsze niż szybkie sortowanie w przypadku list połączonych?
Odpowiedzi:
Wzorzec dostępu do pamięci w Quicksort jest losowy, a także implementacja gotowa do użycia, więc używa wielu zamian, jeśli komórki osiągają uporządkowany wynik.
Jednocześnie sortowanie scalające jest zewnętrzne, wymaga dodatkowej tablicy, aby zwrócić uporządkowany wynik. W tablicy oznacza to dodatkowy narzut miejsca, w przypadku, gdy lista połączona, jest możliwe wyciągnięcie wartości i rozpoczęcie scalania węzłów. Dostęp ma charakter bardziej sekwencyjny.
Z tego powodu szybkie sortowanie nie jest naturalnym wyborem dla listy połączonej, podczas gdy sortowanie po scaleniu ma wielką zaletę.
Notacja Landaua może (mniej więcej, ponieważ Quicksort wciąż jest ) zgadzać się, ale stała jest znacznie wyższa.O ( n2))
W przeciętnym przypadku oba algorytmy są w więc przypadek asymptotyczny jest taki sam, ale preferencja jest ściśle spowodowana ukrytą stałą, a czasem problemem jest stabilność (szybkie sortowanie jest z natury niestabilne, scalanie jest stabilne).O (nlogn )
źródło
Możesz szybko sortować połączone listy, jednak będziesz bardzo ograniczony pod względem wyboru osi przestawnych, ograniczając się do osi przestawnych na początku listy, co jest złe dla prawie posortowanych danych wejściowych, chyba że chcesz zapętlić dwa segmenty dwa razy (raz dla osi przestawnej i raz na partycję). Będziesz musiał zachować stos granic partycji dla list, które nadal musisz posortować. Stos ten może wzrosnąć do gdy wybór przestawny jest zły wraz ze złożonością czasową rosnącą do O ( n 2 ) .O ( n ) O( n2))
Sortowanie korespondencji na połączonych listach można wykonać tylko przy użyciu dodatkowej spacji jeśli zastosujesz podejście oddolne, licząc, gdzie znajdują się granice partycji i odpowiednio scalając.O ( 1 )
Jednak dodając pojedynczą 64-elementową tablicę wskaźników, możesz uniknąć dodatkowej iteracji i posortować listy do elementów w dodatkowej spacji O ( 1 ) .2)64 O ( 1 )
Jest to algorytm używany przez jądro Linuksa do sortowania połączonych list. Chociaż z kilkoma dodatkowymi optymalizacjami, takimi jak ignorowanie
previous
wskaźnika podczas wszystkich operacji scalania oprócz ostatniej.źródło
Możesz napisać sortowanie według scalania, sortowanie według partycji, sortowanie według drzewa i porównywanie wyników.
Łatwo jest napisać sortowanie według drzewa, jeśli pozwalasz na dodatkowe miejsce.
Do sortowania według drzewa każdy węzeł połączonej listy musi mieć dwa wskaźniki, nawet jeśli sortujemy pojedynczo połączoną listę
Na połączonej liście Wolę wstawianie i usuwanie zamiast zamiany
Partycji Hoare można wykonać tylko dla podwójnie połączonej listy
Ten kod wymaga pewnych ulepszeń.
Po pierwsze powinniśmy ograniczyć dodatkową pamięć do potrzeb rekurencyjnych,
a następnie powinniśmy spróbować zastąpić rekurencję iteracją.
Jeśli chcemy dalej ulepszać algorytm, powinniśmy użyć drzewa równoważenia
źródło
Quicksort
Może pokażę kroki do szybkiego sortowania
Jeśli lista zawiera więcej niż jeden węzeł
podrzędne Pierwsza lista zawiera węzły z kluczami mniejszymi niż klucz przestawny
Druga lista zawiera węzły z kluczami równymi kluczowi przestawnemu
Trzecia lista zawiera węzły z kluczami większymi niż klucz przestawny
Ad 1.
Jeśli chcemy szybko wybrać pivot, wybór jest ograniczony.
Możemy wybrać węzeł główny lub węzeł ogonowy.
Nasza lista musi zawierać poiner do węzła, jeśli chcemy, aby nasz pivot
był dostępny szybko, w przeciwnym razie musimy wyszukać węzeł
Ad 2.
Możemy użyć operacji kolejki dla tego kroku. Najpierw usuwamy
węzeł z oryginalnej listy połączonej,
porównujemy jego klucz z kluczem przestawnym i kolejkujemy węzeł do właściwej listy
podrzędnej. Listy podrzędne są tworzone z istniejących węzłów i nie ma potrzeby
przydzielania pamięci dla nowych węzłów
Wskaźnik do węzła ogonowego będzie przydatny, ponieważ operacje kolejek
i konkatenacja przebiegają szybciej przy obecności tego wskaźnika
źródło