Serwis Edukacyjny
Nauczycieli

w I-LO w Tarnowie
obrazek Materiały głownie dla uczniów liceum

  Wyjście       Spis treści       Wstecz       Dalej  

Autor artykułu: mgr Jerzy Wałaszek

©2026 mgr Jerzy Wałaszek

Sortowanie szybkie
  Quick Sort

SPIS TREŚCI
Podrozdziały

Algorytm

Algorytm sortowania szybkiego opiera się na strategii "dziel i zwyciężaj" (ang. divide and conquer), którą możemy krótko scharakteryzować w trzech punktach:

  1. DZIEL - problem główny zostaje podzielony na podproblemy
  2. ZWYCIĘŻAJ - znajdujemy rozwiązanie podproblemów
  3. POŁĄCZ - rozwiązania podproblemów zostają połączone w rozwiązanie problemu głównego

Idea sortowania szybkiego jest następująca:

DZIEL : najpierw sortowany zbiór dzielimy na dwie części w taki sposób, aby wszystkie elementy leżące w pierwszej części (zwanej lewą partycją) były mniejsze lub równe od wszystkich elementów drugiej części zbioru (zwanej prawą partycją).
ZWYCIĘŻAJ : każdą z partycji sortujemy rekurencyjnie tym samym algorytmem.
POŁĄCZ : połączenie tych dwóch partycji w jeden zbiór daje w wyniku zbiór posortowany.
obrazek
prof. Tony Hoare

Sortowanie szybkie zostało wynalezione przez angielskiego informatyka, profesora Tony'ego Hoare'a w latach 60-tych ubiegłego wieku. W przypadku typowym algorytm ten jest najszybszym algorytmem sortującym z klasy złożoności obliczeniowej O(n log n) - stąd pochodzi jego popularność w zastosowaniach. Musimy jednak pamiętać, iż w pewnych sytuacjach (zależnych od sposobu wyboru piwotu oraz niekorzystnego ułożenia danych wejściowych) klasa złożoności obliczeniowej tego algorytmu może się degradować do O(n2), co więcej, poziom wywołań rekurencyjnych może spowodować przepełnienie stosu i zablokowanie komputera. Z tych powodów algorytmu sortowania szybkiego nie można stosować bezmyślnie w każdej sytuacji tylko dlatego, iż jest uważany za jeden z najszybszych algorytmów sortujących - zawsze należy przeprowadzić analizę możliwych danych wejściowych właśnie pod kątem przypadku niekorzystnego - czasem lepszym rozwiązaniem może być zastosowanie wcześniej opisanego algorytmu sortowania przez kopcowanie, który nigdy nie degraduje się do klasy O(n2).

Tworzenie partycji

Do utworzenia partycji musimy ze zbioru wybrać jeden z elementów, który nazwiemy piwotem. W lewej partycji znajdą się wszystkie elementy niewiększe od piwotu, a w prawej partycji umieścimy wszystkie elementy niemniejsze od piwotu. Położenie elementów równych nie wpływa na proces sortowania, zatem mogą one występować w obu partycjach. Również porządek elementów w każdej z partycji nie jest ustalony.

Jako piwot można wybierać element pierwszy, środkowy, ostatni, medianę lub losowy. Dla naszych potrzeb wybierzemy element środkowy:

piwot ← d[(lewy + prawy) div 2]  
piwot  - element podziałowy
d[ ] - dzielony zbiór
lewy - indeks pierwszego elementu
prawy  - indeks ostatniego elementu

Dzielenie na partycje polega na umieszczeniu dwóch wskaźników na początku zbioru - i oraz j. Wskaźnik i przebiega przez zbiór poszukując wartości mniejszych od piwotu. Po znalezieniu takiej wartości jest ona wymieniana z elementem na pozycji j. Po tej operacji wskaźnik j jest przesuwany na następną pozycję. Wskaźnik j zapamiętuje pozycję, na którą trafi następny element oraz na końcu wskazuje miejsce, gdzie znajdzie się piwot. W trakcie podziału piwot jest bezpiecznie przechowywany na ostatniej pozycji w zbiorze.

Przykład:

Dla przykładu podzielimy na partycje zbiór:

{ 7 2 4 7 3 1 4 6 5 8 3 9 2 6 7 6 3 }

Lp. Operacja Opis
1.
 7 2 4 7 3 1 4 68 3 9 2 6 7 6 3 
Wyznaczamy na piwot element środkowy.
2.
 7 2 4 7 3 1 4 68 3 9 2 6 7 6
Piwot wymieniamy z ostatnim elementem
zbioru
3.
 7 2 4 7 3 1 4 6 3 8 3 9 2 6 7 6 i 
Na początku zbioru ustawiamy dwa wskaźniki.
Wskaźnik i będzie przeglądał zbiór do
przedostatniej pozycji. Wskaźnik j zapamiętuje
miejsce wstawiania elementów mniejszych od
piwotu
4.
 74 7 3 1 4 6 3 8 3 9 2 6 7 6   i 
Wskaźnikiem i  szukamy elementu mniejszego
od piwotu
5.
 2 7 4 7 3 1 4 6 3 8 3 9 2 6 7 6   i 
  
Znaleziony element wymieniamy z elementem
na  pozycji j-tej. Po wymianie wskaźnik j
przesuwamy o 1 pozycję.
6.
 2 77 3 1 4 6 3 8 3 9 2 6 7 6     i 
  
Szukamy
7.
 2 4 7 7 3 1 4 6 3 8 3 9 2 6 7 6     i 
    
Wymieniamy i przesuwamy j.
8.
 2 471 4 6 3 8 3 9 2 6 7 6         i 
    
Szukamy
9.
 2 471 4 6 3 8 3 9 2 6 7 6         i 
      
Wymieniamy i przesuwamy j.
10.
 2 4 374 6 3 8 3 9 2 6 7 6           i 
      
Szukamy
11.
 2 4 374 6 3 8 3 9 2 6 7 6           i 
        
Wymieniamy i przesuwamy j.
12.
 2 4 3 176 3 8 3 9 2 6 7 6             i 
        
Szukamy
13.
 2 4 3 176 3 8 3 9 2 6 7 6             i 
          
Wymieniamy i przesuwamy j.
14.
 2 4 3 1 47 68 3 9 2 6 7 6                 i 
          
Szukamy
15.
 2 4 3 1 47 68 3 9 2 6 7 6                 i 
            
Wymieniamy i przesuwamy j.
16.
 2 4 3 1 4 36 7 89 2 6 7 6                     i 
            
Szukamy
17.
 2 4 3 1 4 36 7 89 2 6 7 6                     i 
              
Wymieniamy i przesuwamy j.
18.
 2 4 3 1 4 3 37 8 7 96 7 6                         i 
              
Szukamy
19.
 2 4 3 1 4 3 37 8 7 96 7 6                         i 
                
Wymieniamy i przesuwamy j.
20.
 2 4 3 1 4 3 3 28 7 9 6 6 7 6 7 
                 ^             i   
Lewa partycja     Prawa partycja
Brak dalszych elementów do wymiany. Piwot
wymieniamy z elementem na pozycji j-tej.
Podział na partycje zakończony.

Po zakończeniu podziału na partycje wskaźnik j wyznacza pozycję piwotu. Lewa partycja zawiera elementy mniejsze od piwotu i rozciąga się od początku zbioru do pozycji j - 1. Prawa partycja zawiera elementy większe lub równe piwotowi i rozciąga się od pozycji j + 1 do końca zbioru. Operacja podziału na partycje ma liniową klasę złożoności obliczeniowej - O(n).


do podrozdziału  do strony 

Opis algorytmu

Specyfikacja problemu

Sortuj_szybko(lewy, prawy)

Dane wejściowe

d[ ] - Zbiór zawierający elementy do posortowania. Zakres indeksów elementów jest dowolny.
lewy - indeks pierwszego elementu w zbiorze, lewy ∈ C
prawy - indeks ostatniego elementu w zbiorze, prawy ∈ C

Dane wyjściowe

d[ ] - Zbiór zawierający elementy posortowane rosnąco

Zmienne pomocnicze

piwot - element podziałowy
i, j - indeksy, i, j ∈ C

Lista kroków

K01:
K02: piwot ← d[i];   d[i] ← d[prawy];   j  ← lewy
K03: Dla i = lewy, lewy + 1, ..., prawy - 1:
    wykonuj kroki K04...K05
K04:     Jeśli d[i] ≥ piwot,
    to wykonaj kolejny obieg pętli K03
K05:     d[i] ↔ d[j];    j ← j + 1
K06: d[prawy] ← d[j];   d[j] ← piwot
K07: Jeśli lewy < j - 1,
to Sortuj_szybko(lewy, j - 1)
K08: Jeśli j + 1 < prawy,
to Sortuj_szybko(j + 1, prawy)
K09: Zakończ

Algorytm sortowania szybkiego wywołujemy podając za lewy indeks pierwszego elementu zbioru, a za prawy indeks elementu ostatniego (czyli Sortuj_szybko(1,n)). Zakres indeksów jest dowolny - dzięki temu ten sam algorytm może również sortować fragment zbioru, co wykorzystujemy przy sortowaniu wyliczonych partycji.

Schemat blokowy

obrazek

Na element podziałowy wybieramy element leżący w środku dzielonej partycji. Wyliczamy jego pozycję i zapamiętujemy ją tymczasowo w zmiennej i. Robimy to po to, aby dwukrotnie nie wykonywać tych samych rachunków.

Element d[i] zapamiętujemy w zmiennej piwot, a do d[i] zapisujemy ostatni element partycji. Dzięki tej operacji piwot został usunięty ze zbioru.

Ustawiamy zmienną j na początek partycji. Zmienna ta zapamiętuje pozycję podziału partycji.

W pętli sterowanej zmienną i przeglądamy kolejne elementy od pierwszego do przedostatniego (ostatni został umieszczony na pozycji piwotu, a piwot zapamiętany). Jeśli i-ty element jest mniejszy od piwotu, to trafia on na początek partycji - wymieniamy ze sobą elementy na pozycjach i-tej i j-tej. Po tej operacji przesuwamy punkt podziałowy partycji j.

Po zakończeniu pętli element z pozycji j-tej przenosimy na koniec partycji, aby zwolnić miejsce dla piwotu, po czym wstawiamy tam piwot. Zmienna j wskazuje zatem wynikową pozycję piwotu. Pierwotna partycja została podzielona na dwie partycje:

partycja lewa od pozycji lewy do j - 1 zawiera elementy mniejsze od piwotu
partycja prawa od pozycji j + 1 do pozycji prawy zawiera elementy większe lub równe piwotowi.

Sprawdzamy, czy partycje te obejmują więcej niż jeden element. Jeśli tak, to wywołujemy rekurencyjnie algorytm sortowania szybkiego przekazując mu granice wyznaczonych partycji. Po powrocie z wywołań rekurencyjnych partycja wyjściowa jest posortowana rosnąco. Kończymy algorytm.


do podrozdziału  do strony 

Przykładowe programy

C++
// Sortowanie Szybkie
//-------------------
// (C)2012 mgr Jerzy Wałaszek
// I Liceum Ogólnokształcące
// w Tarnowie

#include <iostream>
#include <iomanip>
#include <cstdlib>
#include <time.h>

using namespace std;

const int N = 20; // Liczebność zbioru.

int d[N];

// Procedura sortowania szybkiego
//-------------------------------

void Sortuj_szybko(int lewy, int prawy)
{
  int i,j,piwot;

  i = (lewy + prawy) / 2;
  piwot = d[i];
  d[i] = d[prawy];
  for(j = i = lewy; i < prawy; i++)
    if(d[i] < piwot)
    {
      swap(d[i], d[j]);
      j++;
    }
  d[prawy] = d[j];
  d[j] = piwot;
  if(lewy < j - 1)  Sortuj_szybko(lewy, j - 1);
  if(j + 1 < prawy) Sortuj_szybko(j + 1, prawy);
}

// Program główny
//---------------

int main()
{
  int i;

  srand((unsigned)time(NULL));

  cout << "   Sortowanie szybkie\n"
          "------------------------\n"
          " (C)2005 Jerzy Walaszek \n\n"
          "Przed sortowaniem:\n\n";

  // Najpierw wypełniamy tablicę d[]
  // liczbami pseudolosowymi, a następnie
  // wyświetlamy jej zawartość

  for(i = 0; i < N; i++)
    d[i] = rand() % 100;
  for(i = 0; i < N; i++)
    cout << setw(4) << d[i];
  cout << endl << endl;

  // Sortujemy

  Sortuj_szybko(0,N - 1);

  // Wyświetlamy wynik sortowania

  cout << "Po sortowaniu:\n\n";
  for(i = 0; i < N; i++)
    cout << setw(4) << d[i];
  cout << endl << endl;
  system("pause");
  return 0;
}
Pascal
// Sortowanie Szybkie
//-------------------
// (C)2012 mgr Jerzy Wałaszek
// I Liceum Ogólnokształcące
// w Tarnowie

program Quick_Sort;

const N = 20; // Liczebność zbioru.

var
  d : array[1..N] of integer;

// Procedura sortowania szybkiego
//-------------------------------

procedure Sortuj_szybko(lewy, prawy : integer);
var
  i,j,piwot,x : integer;
begin
  i := (lewy + prawy) div 2;
  piwot := d[i];
  d[i] := d[prawy];
  j := lewy;
  for i := lewy to prawy - 1 do
    if d[i] < piwot then
    begin
      x := d[i];
      d[i] := d[j];
      d[j] := x;
      inc(j);
    end;
  d[prawy] := d[j];
  d[j] := piwot;
  if lewy < j - 1 then
    Sortuj_szybko(lewy, j - 1);
  if j + 1 < prawy then
    Sortuj_szybko(j + 1, prawy);
end;

// Program główny
//---------------

var
  i : integer;
begin
  writeln('   Sortowanie szybkie');
  writeln('------------------------');
  writeln(' (C)2005 Jerzy Walaszek ');
  writeln;

  // Najpierw wypełniamy tablicę d[]
  // liczbami pseudolosowymi, a następnie
  // wyświetlamy jej zawartość

  randomize;
  for i := 1 to N do
    d[i] := random(100);
  writeln('Przed sortowaniem:');
  writeln;
  for i := 1 to N do
    write(d[i] : 4);
  writeln;
  writeln;

  // Sortujemy

  Sortuj_szybko(1,N);

  // Wyświetlamy wynik sortowania

  writeln('Po sortowaniu:');
  writeln;
  for i := 1 to N do
    write(d[i] : 4);
  writeln;
  writeln;
  writeln('Nacisnij Enter...');
  readln;
end.
Basic
' Sortowanie szybkie
'-------------------
' (C)2012 mgr Jerzy Wałaszek
' I Liceum Ogólnokształcące
' w Tarnowie

DECLARE SUB Sortuj_szybko(lewy AS INTEGER,_
                          prawy AS INTEGER)

CONST N = 20 ' liczebność zbioru
DIM SHARED d(N) AS INTEGER
DIM i AS INTEGER

PRINT "  Sortowanie  szybkie"
PRINT "-----------------------"
PRINT "(C)2005  Jerzy Walaszek"
PRINT
PRINT "Przed sortowaniem:"
PRINT

' Wypełniamy tablicę liczbami
' pseudolosowymi i wyświetlamy je

RANDOMIZE
FOR i = 1 TO N
  d(i) = INT(RND * 100)
  PRINT USING "####";d(i);
NEXT
PRINT
PRINT

' Sortujemy

Sortuj_szybko(1,N)

' Wyświetlamy wynik sortowania

PRINT "Po sortowaniu:"
PRINT
FOR i = 1 TO N
  PRINT USING "####";d(i);
NEXT
PRINT
PRINT
PRINT "Nacisnij Enter..."
SLEEP
END

' Procedura sortowania szybkiego
'-------------------------------

SUB Sortuj_szybko(lewy AS INTEGER, _
                  prawy AS INTEGER)

DIM AS INTEGER i, j, piwot

  i = (lewy + prawy) \ 2
  piwot = d(i)
  d(i) = d(prawy)
  j = lewy
  FOR i = lewy TO prawy - 1
    IF d(i) < piwot THEN
      SWAP d(i), d(j)
      j += 1
    END IF
  NEXT
  d(prawy) = d(j)
  d(j) = piwot
  IF lewy < j - 1  THEN _
    Sortuj_szybko(lewy, j - 1)
  IF j + 1 < prawy THEN _
    Sortuj_szybko(j + 1, prawy)

END SUB
Python (dodatek)
# Sortowanie szybkie
#-------------------
# (C)2012 mgr Jerzy Wałaszek
# I Liceum Ogólnokształcące
# w Tarnowie

import random

n = 20 # liczebność zbioru
d = [random.randrange(100) for i in range(n)]

# Procedura sortowania szybkiego
#-------------------------------

def sortuj_szybko(lewy,prawy):
    i = (lewy + prawy) // 2
    piwot = d[i]
    d[i] = d[prawy]
    j = lewy
    for i in range(lewy,prawy):
        if d[i] < piwot:
            d[i],d[j] = d[j],d[i]
            j += 1
    d[prawy] = d[j]
    d[j] = piwot
    if lewy < j - 1:
        sortuj_szybko(lewy, j - 1)
    if j + 1 < prawy:
        sortuj_szybko(j + 1, prawy)

# program główny

print(" Sortowanie  szybkie")
print("---------------------")
print("(C)2026 Jerzy Wałaszek")
print()
print("Przed sortowaniem:")
print()
for i in range(n):
    print("%4d" % (d[i]),end="")
print()
print()

# Sortujemy

sortuj_szybko(0,n - 1)

# Wyświetlamy wynik sortowania

print("Po sortowaniu:")
print()
for i in range(n):
    print("%4d" % (d[i]),end="")
print()
print()
input("Naciśnij Enter...")
Wynik:
 Sortowanie  szybkie
---------------------
(C)2026 Jerzy Wałaszek

Przed sortowaniem:

  21  54  97  49  62  31  76  58  51   9  34  55  31  46  76  15  75  29   1  13

Po sortowaniu:

   1   9  13  15  21  29  31  31  34  46  49  51  54  55  58  62  75  76  76  97

Naciśnij Enter...
JavaScript
<html>
  <head>
  </head>
  <body>
    <form style="BORDER-RIGHT: #ff9933 1px outset;
                 PADDING-RIGHT: 4px; BORDER-TOP: #ff9933 1px outset;
                 PADDING-LEFT: 4px; PADDING-BOTTOM: 1px;
                 BORDER-LEFT: #ff9933 1px outset; PADDING-TOP: 1px;
                 BORDER-BOTTOM: #ff9933 1px outset;
                 BACKGROUND-COLOR: #ffcc66" name="frmquicksort">
      <h3 style="text-align: center">Sortowanie Szybkie</h3>
      <p style="TEXT-ALIGN: center">
        (C)2012 mgr Jerzy Wałaszek - I LO w Tarnowie
      </p>
      <hr>
      <p style="TEXT-ALIGN: center">
        <input onclick="main()" type="button" value="Sortuj" name="B1">
      </p>
      <p id="t_out" style="TEXT-ALIGN: center">...</p>
    </form>

<script language=javascript>

// Sortowanie Szybkie
//-------------------
// (C)2012 mgr Jerzy Wałaszek
// I Liceum Ogólnokształcące
// w Tarnowie

var N = 20; // Liczebność zbioru.
var d = new Array(N)

// Procedura sortowania szybkiego
//-------------------------------

function Sortuj_szybko(lewy, prawy)
{
  var i,j,piwot,x;

  i = Math.floor((lewy + prawy) / 2);
  piwot = d[i]; d[i] = d[prawy];
  for(j = i = lewy; i < prawy; i++)
  if(d[i] < piwot)
  {
    x = d[i]; d[i] = d[j]; d[j] = x;
    j++;
  }
  d[prawy] = d[j]; d[j] = piwot;
  if(lewy < j - 1)  Sortuj_szybko(lewy, j - 1);
  if(j + 1 < prawy) Sortuj_szybko(j + 1, prawy);
}

// Program główny
//---------------

function main()
{
  var i,t;

// Najpierw wypełniamy tablicę d[] liczbami pseudolosowymi
// a następnie wyświetlamy jej zawartość

  for(i = 0; i < N; i++) d[i] = Math.floor(Math.random() * 100);
  t = "Przed sortowaniem:<BR><BR>";
  for(i = 0; i < N; i++) t += d[i] + " ";

// Sortujemy

  Sortuj_szybko(0, N - 1);

// Wyświetlamy wynik sortowania

  t += "<BR><BR>Po sortowaniu:<BR><BR>";
  for(i = 0; i < N; i++) t += d[i] + " ";
  document.getElementById("t_out").innerHTML = t
}

</script> 

  </body>
</html>

Sortowanie Szybkie

(C)2012 mgr Jerzy Wałaszek - I LO w Tarnowie


...


do podrozdziału  do strony 

Podsumowanie

Cechy Algorytmu Sortowania Szybkiego
klasa złożoności obliczeniowej optymistyczna O(n log n)
klasa złożoności obliczeniowej typowa
klasa złożoności obliczeniowej pesymistyczna O(n2)
Sortowanie w miejscu TAK
Stabilność NIE

do podrozdziału  do strony 

Zadania dla ambitnych

  1. Spróbuj znaleźć przypadek pesymistyczny dla algorytmu sortowania szybkiego opisanego w tym rozdziale.
  2. Przebadaj algorytmy sortowania szybkiego, w których piwot wybierany jest:
    1. Na początku partycji

    2. Na końcu partycji

    3. W miejscu losowym wewnątrz partycji

  3. Uzasadnij, iż algorytm sortowania szybkiego nie posiada cechy stabilności.
  4. Wyszukaj w Internecie informację na temat algorytmu Introsort.

do podrozdziału  do strony 

Zespół Przedmiotowy
Chemii-Fizyki-Informatyki

w I Liceum Ogólnokształcącym
im. Kazimierza Brodzińskiego
w Tarnowie
ul. Piłsudskiego 4
©2026 mgr Jerzy Wałaszek

Materiały tylko do użytku dydaktycznego. Ich kopiowanie i powielanie jest dozwolone pod warunkiem podania źródła oraz niepobierania za to pieniędzy.
Pytania proszę przesyłać na adres email: i-lo@eduinf.waw.pl
Serwis wykorzystuje pliki cookies. Jeśli nie chcesz ich otrzymywać, zablokuj je w swojej przeglądarce.

Informacje dodatkowe.