Algorytm sortowania sterty (z kodem w Python oraz C++)

Co to jest algorytm sortowania sterty?

Heap Sort to jeden z popularnych i szybszych algorytmรณw sortowania. Jest zbudowany na kompletnej strukturze danych drzewa binarnego. Poszukamy maksymalnego elementu i umieล›cimy go na szczycie sterty maksymalnej. Umieล›cimy go na wฤ™ลบle nadrzฤ™dnym drzewa binarnego.

Powiedzmy, ลผe podana jest tablica, dane = [10,5, 7, 9, 4, 11, 45, 17, 60].

W tablicy, jeล›li i-ty (i=0,1,2,3 โ€ฆ) indeks jest wฤ™zล‚em nadrzฤ™dnym, to (2i+1) i (2i+2) bฤ™dฤ… lewym i prawym dzieckiem. Tworzenie kompletnego drzewa binarnego za pomocฤ… tej tablicy bฤ™dzie wyglฤ…daฤ‡ nastฤ™pujฤ…co:

Algorytm sortowania na stosie

Wykonamy proces heapify od poczฤ…tku do koล„ca tablicy. Poczฤ…tkowo, jeล›li przekonwertujemy tablicฤ™ na drzewo, bฤ™dzie wyglฤ…daฤ‡ jak powyลผej. Moลผemy zobaczyฤ‡, ลผe nie utrzymuje ลผadnej wล‚aล›ciwoล›ci heap (min-heap lub max heap). Otrzymamy posortowanฤ… tablicฤ™, wykonujฤ…c proces heapify dla wszystkich wฤ™zล‚รณw.

Zastosowanie sortowania stertowego

Oto kilka zastosowaล„ algorytmu sortowania sterty:

  • Konstrukcja โ€žkolejek priorytetowychโ€ wymaga sortowania sterty. Poniewaลผ heapsort utrzymuje element posortowany po kaลผdym wstawieniu.
  • Struktura danych sterty jest skuteczna w znajdowaniu kth najwiฤ™kszy element w danej tablicy.
  • Jฤ…dro Linuksa domyล›lnie uลผywa sortowania przez stertฤ™ algorytm sortowania poniewaลผ ma zล‚oลผonoล›ฤ‡ przestrzennฤ… O (1).

Utwรณrz sortowanie sterty na przykล‚adzie

Tutaj utworzymy kopiec maksymalny z nastฤ™pujฤ…cego kompletnego drzewa binarnego.

Utwรณrz sortowanie sterty na przykล‚adzie

Wฤ™zล‚y liล›ciowe to 17, 60, 4, 11 i 45. Nie majฤ… ลผadnych wฤ™zล‚รณw podrzฤ™dnych. Dlatego sฤ… wฤ™zล‚ami liล›ciowymi. Wiฤ™c rozpoczniemy metodฤ™ heapify od ich wฤ™zล‚a nadrzฤ™dnego. Oto kroki:

Krok 1) Wybierz poddrzewo znajdujฤ…ce siฤ™ najbardziej na lewo. Jeล›li wฤ™zล‚y podrzฤ™dne sฤ… wiฤ™ksze, zamieล„ wฤ™zeล‚ nadrzฤ™dny z wฤ™zล‚em podrzฤ™dnym.

Tutaj wฤ™zeล‚ nadrzฤ™dny to 9. Wฤ™zล‚y podrzฤ™dne to 17 i 60. Poniewaลผ 60 jest najwiฤ™ksze, 60 i 9 zostanฤ… zamienione miejscami, aby zachowaฤ‡ maksymalna sterta.

Utwรณrz sortowanie sterty na przykล‚adzie

Krok 2) Teraz poddrzewo znajdujฤ…ce siฤ™ najbardziej po lewej stronie jest stertowane. Nastฤ™pny wฤ™zeล‚ nadrzฤ™dny to 7. Ten rodzic ma dwa wฤ™zล‚y podrzฤ™dne, a najwiฤ™kszy ma 45. Zatem 45 i 7 zostanฤ… zamienione.

Utwรณrz sortowanie sterty na przykล‚adzie

Utwรณrz sortowanie sterty na przykล‚adzie

Krok 3) Wฤ™zล‚y 60 i 4 majฤ… wฤ™zeล‚ nadrzฤ™dny 5. Poniewaลผ โ€ž5โ€ jest mniejsze niลผ wฤ™zeล‚ podrzฤ™dny 60, zostanie ono zamienione.

Utwรณrz sortowanie sterty na przykล‚adzie

Utwรณrz sortowanie sterty na przykล‚adzie

Krok 4) Teraz wฤ™zeล‚ 5 ma wฤ™zeล‚ podrzฤ™dny 17,9. To nie jest zachowanie wล‚aล›ciwoล›ci max sterty. Zatem 5 zostanie zastฤ…pione 17.

Utwรณrz sortowanie sterty na przykล‚adzie

Krok 5) Wฤ™zeล‚ 10 zostanie zamieniony z wฤ™zล‚em 60, a nastฤ™pnie z wฤ™zล‚em 17. Proces bฤ™dzie wyglฤ…daล‚ nastฤ™pujฤ…co.

Utwรณrz sortowanie sterty na przykล‚adzie

Utwรณrz sortowanie sterty na przykล‚adzie

Krok 6) Do kroku 5 utworzyliล›my maksymalnฤ… stertฤ™. Kaลผdy wฤ™zeล‚ nadrzฤ™dny jest wiฤ™kszy niลผ jego wฤ™zล‚y podrzฤ™dne. Wฤ™zeล‚ gล‚รณwny ma maksymalnฤ… wartoล›ฤ‡ (60).

Uwaga: Aby utworzyฤ‡ posortowanฤ… tablicฤ™, musimy zastฤ…piฤ‡ wฤ™zeล‚ o maksymalnej wartoล›ci jego nastฤ™pcฤ….

Ten proces nazywa siฤ™ โ€žekstrakt maksโ€. Poniewaลผ 60 to maksymalny wฤ™zeล‚, ustalimy jego pozycjฤ™ na 0-tym indeksie i utworzymy stertฤ™ bez wฤ™zล‚a 60.

Utwรณrz sortowanie sterty na przykล‚adzie

Utwรณrz sortowanie sterty na przykล‚adzie

Krok 7) Po usuniฤ™ciu 60 nastฤ™pna maksymalna wartoล›ฤ‡ wyniesie 45. Ponownie wykonamy proces โ€žWyodrฤ™bnij Maxโ€ z wฤ™zล‚a 45.

Tym razem otrzymamy 45 i zastฤ…pimy wฤ™zeล‚ gล‚รณwny jego nastฤ™pcฤ… 17.

Musimy wykonaฤ‡ โ€žWyciฤ…g maksโ€, aลผ wszystkie elementy zostanฤ… posortowane.

Po wykonaniu tych krokรณw aลผ do wyodrฤ™bnienia wszystkich wartoล›ci maksymalnych otrzymamy nastฤ™pujฤ…cฤ… tablicฤ™.

Utwรณrz sortowanie sterty na przykล‚adzie

Co to jest sterta binarna?

Kopia binarna jest swego rodzaju kompletna drzewo binarne struktura danych. W tego rodzaju strukturze drzewa wฤ™zeล‚ nadrzฤ™dny jest wiฤ™kszy lub mniejszy niลผ wฤ™zล‚y podrzฤ™dne. Jeล›li wฤ™zeล‚ nadrzฤ™dny jest mniejszy, wรณwczas sterta nazywa siฤ™ โ€žStertฤ… minimalnฤ…โ€, a jeล›li wฤ™zeล‚ nadrzฤ™dny jest wiฤ™kszy, stertฤ™ nazywa siฤ™ โ€žStertฤ… maksymalnฤ…โ€.

Oto przykล‚ady minimalnej i maksymalnej sterty.

Minimalna sterta i maksymalna sterta
Minimalna sterta i maksymalna sterta

Jeล›li na powyลผszym rysunku zauwaลผysz โ€žMin Heapโ€, wฤ™zeล‚ nadrzฤ™dny jest zawsze mniejszy niลผ jego wฤ™zล‚y podrzฤ™dne. Na czele drzewa moลผemy znaleลบฤ‡ najmniejszฤ… wartoล›ฤ‡ 10.

Podobnie w przypadku โ€žMaksymalnej stertyโ€ wฤ™zeล‚ nadrzฤ™dny jest zawsze wiฤ™kszy niลผ wฤ™zล‚y podrzฤ™dne. Maksymalny element wystฤ™puje w wฤ™ลบle gล‚รณwnym โ€žMax Heapโ€.

Czym jest โ€žHeapifyโ€?

โ€žHeapifyโ€ to zasada kopca, ktรณra zapewnia pozycjฤ™ wฤ™zล‚a. W Heapify, maksymalny kopiec zawsze utrzymuje relacjฤ™ z rodzicem i dzieckiem, a wฤ™zeล‚ rodzica bฤ™dzie wiฤ™kszy niลผ wฤ™zล‚y dzieci.

Na przykล‚ad, jeล›li dodany zostanie nowy wฤ™zeล‚, musimy zmieniฤ‡ ksztaล‚t sterty. Jednak moลผemy potrzebowaฤ‡ zmieniฤ‡ lub zamieniฤ‡ wฤ™zล‚y lub przearanลผowaฤ‡ tablicฤ™. Ten proces zmiany ksztaล‚tu sterty nazywa siฤ™ โ€žheapifyโ€.

Oto przykล‚ad dziaล‚ania heapify:

Dodawanie nowego wฤ™zล‚a i heapify
Dodawanie nowego wฤ™zล‚a i heapify

Oto kroki dla heapify:

Krok 1) Dodano wฤ™zeล‚ 65 jako prawy element podrzฤ™dny wฤ™zล‚a 60.

Krok 2) Sprawdลบ, czy nowo dodany wฤ™zeล‚ jest wiฤ™kszy niลผ rodzic.

Krok 3) Poniewaลผ jest wiฤ™kszy niลผ wฤ™zeล‚ nadrzฤ™dny, zamieniliล›my wล‚aล›ciwe dziecko z jego rodzicem.

Jak zbudowaฤ‡ stertฤ™

Zanim zbudujemy stos lub utworzymy stos drzewa, musimy wiedzieฤ‡, jak bฤ™dziemy go przechowywaฤ‡. Poniewaลผ stos jest kompletnym drzewem binarnym, lepiej jest uลผyฤ‡ szyk do przechowywania danych sterty.

Zaล‚รณลผmy, ลผe tablica zawiera ล‚ฤ…cznie n elementรณw. Jeล›li โ€žiโ€ indeks jest wฤ™zล‚em nadrzฤ™dnym, wรณwczas lewy wฤ™zeล‚ bฤ™dzie miaล‚ indeks (2i+1), a prawy wฤ™zeล‚ bฤ™dzie w indeksie (2i+2). Zakล‚adamy, ลผe indeks tablicy zaczyna siฤ™ od 0.

Uลผywajฤ…c tego, zapiszemy maksymalnฤ… iloล›ฤ‡ stosu w nastฤ™pujฤ…cy sposรณb:

Oparta na tablicach reprezentacja maksymalnej sterty
Oparta na tablicy reprezentacja maksymalnej sterty

Algorytm heapify utrzymuje wล‚aล›ciwoล›ฤ‡ heap. Jeล›li rodzic nie ma wartoล›ci skrajnej (mniejszej lub wiฤ™kszej), zostanie zamieniony z najbardziej skrajnym wฤ™zล‚em podrzฤ™dnym.

Oto kroki sล‚uลผฤ…ce do utworzenia kopca maksymalnego:

Krok 1) Zacznij od wฤ™zล‚a liล›cia.

Krok 2) Znajdลบ maksimum miฤ™dzy rodzicem a dzieฤ‡mi.

Krok 3) Zamieล„ wฤ™zล‚y, jeล›li wฤ™zeล‚ podrzฤ™dny ma wiฤ™kszฤ… wartoล›ฤ‡ niลผ wฤ™zeล‚ nadrzฤ™dny.

Krok 4) Przejdลบ o jeden poziom wyลผej.

Krok 5) Postฤ™puj zgodnie z krokami 2,3,4, aลผ osiฤ…gniemy indeks 0 lub posortuj caล‚e drzewo.

Oto pseudokod dla rekurencyjnego heapify (maksymalny heap):

def heapify():
  inputโ†’ array, size, i
  largest = i
  left = 2*i + 1
  right = 2*i + 2
if left<n and array[largest ] < array[left]:
  largest = left
if right<n and array[largest ] < array[right]:
  largest = right
If largest not equals i:
  swap(array[i],array[largest])
  heapify(array,n,largest)

Pseudokod sortowania sterty

Oto pseudokod algorytmu sortowania sterty:

Heapify(numbers as an array, n as integer, i as integer):
  largest = i
  left = 2i+1
  right= 2i+2
if(left<=n) and (numbers[i]<numbers[left])
  largest=left
if(right<=n) and (numbers[i]<numbers[right])
  largest=right
if(largest  != i)
  swap(numbers[i], numbers[largest])
  Heapify(numbers,n,largest)
HeapSort(numbers as an array):
  n= numbers.size()
for i in range n/2 to 1
  Heapify(numbers,n,i)
for i in range n to 2
  Swap numbers[i] with numbers[1]
  Heapify(numbers,i,0)

Przykล‚ad kodu sortowania sterty w C++

#include <iostream>
using namespace std;
void display(int arr[], int n)
{
    for (int i = 0; i < n; i++)
    {
        cout << arr[i] << "\t";
    }
    cout << endl;
}
void heapify(int numbers[], int n, int i)
{
    int largest = i;
    int left = 2 * i + 1;
    int right = 2 * i + 2;
    if (left < n && numbers[left] < numbers[largest])
    {
        largest = left;
    }
    if (right < n && numbers[right] < numbers[largest])
    {
        largest = right;
    }
    if (largest != i)
    {
	//uncomment the following line to see details in output
        //cout<<"Swapping "<< numbers[i]<< " and "<<numbers[largest]<<endl;
        swap(numbers[i], numbers[largest]);
        heapify(numbers, n, largest);
    }
}
void heapSort(int numbers[], int n)
{
    for (int i = n/2 - 1; i >= 0; i--)
    {
        heapify(numbers, n, i);
//uncomment the following line to see details in output
 //cout<<"Heapify:\t";
  //display(numbers,n);
    }
    for (int i = n - 1; i >= 0; i--)
    {
        swap(numbers[0], numbers[i]);
        heapify(numbers, i, 0);
    }
}
int main()
{
    int numbers[] = { 10,5, 7, 9, 4, 11, 45, 17, 60};
    int size = sizeof(numbers) / sizeof(numbers[0]);
    cout<<"Initial Array:\t";
    display(numbers,size);
    heapSort(numbers, size);
    cout<<"Sorted Array (descending order):\t";
    display(numbers, size);
}

Wyjล›cie:

Initial Array:  10      5       7       9       4       11      45      17      60
Sorted Array (descending order):  60      45      17      11      10      9       7       5       4

Przykล‚ad kodu sortowania sterty w Python

def display(arr):
    for i in range(len(arr)):
    print(arr[i], end = "\t")
print()
def heapify(numbers, n, i):
    largest = i
left = 2 * i + 1
right = 2 * i + 2
if left < n and numbers[left] < numbers[largest]:
    largest = left
if right < n and numbers[right] < numbers[largest]:
    largest = right
if largest != i:
    numbers[i], numbers[largest] = numbers[largest], numbers[i]
heapify(numbers, n, largest)
def heapSort(items, n):
    for i in range(n //2,-1,-1):
        heapify(items, n, i) for i in range(n - 1, -1, -1):
        items[0], items[i] = items[i], items[0] heapify(items, i, 0) numbers = [10, 5, 7, 9, 4, 11, 45, 17, 60] print("Initial List:\t", end = "") display(numbers) print("After HeapSort:\t", end = "") heapSort(numbers, len(numbers)) display(numbers)

Wyjล›cie:

Initial List:   10      5       7       9       4       11      45      17      60
After HeapSort: 60      45      17      11      10      9       7       5       4

Analiza zล‚oลผonoล›ci czasowej i przestrzennej sortowania kopcowego

Istnieje zล‚oลผonoล›ฤ‡ czasowa i zล‚oลผonoล›ฤ‡ przestrzenna, ktรณre moลผemy analizowaฤ‡ dla sortowania kopcowego. Dla zล‚oลผonoล›ci czasowej mamy nastฤ™pujฤ…ce przypadki:

  1. Najlepsza sprawa
  2. ลšrednia sprawa
  3. Najgorszy przypadek

Sterta jest zaimplementowana na kompletnym drzewie binarnym. Zatem na dolnym poziomie drzewa binarnego bฤ™dzie maksymalna liczba wฤ™zล‚รณw. Jeล›li dolny poziom ma n wฤ™zล‚รณw, wรณwczas wyลผszy poziom bฤ™dzie miaล‚ n/2 wฤ™zล‚รณw.

Analiza zล‚oลผonoล›ci czasu i przestrzeni

W tym przykล‚adzie poziom 3 ma cztery elementy, poziom 2 ma dwa elementy, a poziom 1 ma jeden element. Jeล›li istnieje caล‚kowita liczba n elementรณw, wysokoล›ฤ‡ lub caล‚kowity poziom bฤ™dzie wynosiฤ‡ Zaloguj2(N). Zatem wstawienie pojedynczego elementu moลผe wymagaฤ‡ maksymalnie iteracji Log(n).

Gdy chcemy pobraฤ‡ maksymalnฤ… wartoล›ฤ‡ ze sterty, po prostu bierzemy wฤ™zeล‚ gล‚รณwny. Nastฤ™pnie ponownie uruchamiamy heapify. Kaลผda heapify pobiera Zaloguj2(n) czas. Wyodrฤ™bnienie maksimum zajmuje czas O(1).

Najlepszy przypadek zล‚oลผonoล›ci czasowej dla algorytmu sortowania kopcowego

Kiedy wszystkie elementy tablicy sฤ… juลผ posortowane, zbudowanie sterty zajmie O(n) czasu. Poniewaลผ jeล›li lista jest posortowana, wstawienie elementu zajmie staล‚y czas, czyli O(1).

Zatem w najlepszym przypadku utworzenie maksymalnej lub minimalnej sterty zajmie O(n) czasu.

ลšrednia zล‚oลผonoล›ฤ‡ czasowa przypadku dla algorytmu sortowania kopcowego

Wstawienie elementu lub wydobycie maksimum kosztuje O(log(n)) czasu. Zatem ล›rednia zล‚oลผonoล›ฤ‡ czasowa przypadku dla algorytmu sortowania kopcowego wynosi O(n log(n)).

Najgorszy przypadek zล‚oลผonoล›ci czasowej dla algorytmu sortowania kopcowego

Podobnie jak w przypadku przeciฤ™tnym, w najgorszym scenariuszu moglibyล›my wykonaฤ‡ heapify n razy. Kaลผde heapify bฤ™dzie kosztowaฤ‡ O(log(n)) czasu. Tak wiฤ™c zล‚oลผonoล›ฤ‡ czasowa w najgorszym przypadku bฤ™dzie wynosiฤ‡ O(n log(n)).

Zล‚oลผonoล›ฤ‡ przestrzenna dla algorytmu sortowania kopcowego

Sortowanie kopcowe to algorytm zaprojektowany w miejscu. Oznacza to, ลผe do wykonania zadania nie jest potrzebna ลผadna dodatkowa ani tymczasowa pamiฤ™ฤ‡. Jeล›li przyjrzymy siฤ™ implementacji, zauwaลผymy, ลผe uลผyliล›my swap() do wykonania wymiany wฤ™zล‚รณw. Nie byล‚a potrzebna ลผadna inna lista ani tablica. Zatem zล‚oลผonoล›ฤ‡ przestrzenna wynosi O(1).

Podsumuj ten post nastฤ™pujฤ…co: