QuickSort również dzieli problem na mniejsze, ale inaczej niż MergeSort: wybiera pivot (element rozdzielający), a następnie partycjonuje tablicę tak, by elementy mniejsze od pivota były po lewej, a większe po prawej. Sam pivot trafia na docelową pozycję, a algorytm rekurencyjnie sortuje obie strony.
Jeśli pivot zawsze trafia na najmniejszy lub największy element (np. przy już posortowanej tablicy i wyborze pierwszego elementu jako pivota), podział jest bardzo nierówny i złożoność spada do O(n²). Dlatego w praktyce pivot wybiera się losowo lub jako medianę z kilku próbek.
| Przypadek | Złożoność | Kiedy występuje |
|---|---|---|
| Średni | O(n log n) | Pivot dzieli tablicę w miarę równo |
| Najgorszy | O(n²) | Pivot zawsze skrajny (źle posortowane/dobrane dane) |
Jeśli wiemy, że sortowane liczby mieszczą się w ograniczonym, niewielkim zakresie (np. oceny 1–6, wiek 0–120), można posortować je w czasie liniowym O(n + k), gdzie k to rozpiętość zakresu — bez żadnych porównań elementów między sobą!
Tylko gdy zakres wartości k jest mały w porównaniu z n. Dla k rzędu milionów (np. sortowanie dowolnych liczb całkowitych 32-bitowych) tablica pomocnicza byłaby zbyt duża — wtedy lepiej użyć MergeSort/QuickSort.
Zaimplementowana wyżej wersja QuickSort tworzy nowe listy (mniejsze/rowne/wieksze), co zużywa dodatkową pamięć. Zbadaj (w dokumentacji lub eksperymentalnie), na czym polega wersja "in-place" z zamianą elementów miejscami wewnątrz oryginalnej tablicy — jaka jest jej przewaga?
Mając listę ocen 30 uczniów (wartości 1–6), posortuj ją algorytmem Counting Sort i policz średnią arytmetyczną. Porównaj czas wykonania z wbudowaną funkcją sorted() dla dużej (100 000-elementowej) listy ocen.
1. Jaka jest średnia złożoność czasowa QuickSort?
2. Kiedy QuickSort osiąga najgorszy przypadek O(n²)?
3. Counting Sort osiąga złożoność liniową, ponieważ: