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:
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.
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.
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.
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.
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.
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.
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.
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ฤ.
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.

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:

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:

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:
- Najlepsza sprawa
- ลrednia sprawa
- 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.
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).














