Powyższa minimalna sterta opisuje stos, w którym liczba dzieci na węzeł nadrzędny może wynosić więcej niż dwa. Sterta binarna to jedna, w której największa liczba dzieci na węzeł nadrzędny to dwa. Kompletna sterta binarna to taka, w której każdy węzeł ma dwoje dzieci, z wyjątkiem, że na najniższym poziomie może istnieć tylko jeden węzeł liściowy bez rodzeństwa; z resztą najniższych węzłów liściowych sparowanych i zaczynających się od skrajnego lewego końca ostatniego rzędu, kończąc się tym węzłem z pojedynczym liściem, który musi być lewym węzłem.
Awatyfikacja oznacza budowanie (tworzenie) sterty. Ponieważ drzewo, w którym każdy węzeł może mieć dowolną liczbę dzieci, może zostać przekształcone w pełne drzewo binarne, prawdziwym celem tego artykułu jest wytworzenie złożoności czasowej w zakresie rozliczania pełnego drzewa binarnego.
Przykładowym schematem kompletnego drzewa binarnego jest:
Każdy węzeł liściowy na najniższym poziomie, który nie ma rodzeństwa, musi być lewym węzłem. Wszystkie węzły ostatniego rzędu, w tym możliwy pojedynczy lewy węzeł, są „spłukiwane” na lewym końcu ostatniego rzędu.
Zgodnie z definicją sterty lewe rodzeństwo może być mniejsze, większe lub równe prawemu rodzeństwu. Zamówienie obu rodzeństwa nie jest określone.
Pełne drzewo binarne
Powyższe drzewo jest kompletnym drzewem binarnym, ale nie jest pełnym drzewem binarnym. Jest to również minimalna stos. Gdyby było to pełne drzewo binarne, to wszystkie węzły poziomu ostatniego, ale jeden by miałyby dwoje dzieci. Powyższe drzewo jest odrana poniżej jako pełne drzewo binarne:
Drzewo może być reprezentowane przez tablicę. Drzewo jest odczytane od góry do dołu, od lewej do prawej, wiersz po wierszu; następnie umieszczone w tablicy. Poniższa tabela pokazuje szereg treści i indeksów dla tego drzewa:
4 | 6 | 12 | 8 | 7 | 16 | 15 | 23 | 10 | 20 | 18 | 25 | zero | zero | zero |
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
Wartości komórkowe znajdują się w pierwszym rzędzie tabeli. Indeksy oparte na zero znajdują się w drugim rzędzie tabeli.
Związek między indeksem rodzica a jego indeksami dzieci
W drzewie lub odpowiadającej tablicy root jest w indeksie 0 dla indeksowania opartego na zerowym. Niech będę zmienną do przechowywania każdego indeksu. Dzieci korzenia znajdują się w indeksach 1 i 2, które wynoszą 0 + 1 i 0 + 2. Dzieci węzła 1 znajdują się w indeksach 3 i 4, które wynoszą 1 + 2 i 1 + 3. Dzieci węzła 2 są w indeksie 5 i 6, które wynoszą 2 + 3 i 2 + 4. Dzieci węzła 3 są w indeksie 7 i 8, które wynoszą 3 + 4 i 3 + 5. Dzieci węzła 4 są w indeksie 9 i 10, które wynoszą 4 + 5 i 4 + 6; i tak dalej.
Niech na razie będę indeksem nadrzędnym. Więc dzieci każdego rodzica są w indeksie I + i + 1 i na i + i + 2, które są:
2i +1 i 2i +2
Odwrotność powinna być znana. To znaczy, biorąc pod uwagę indeks, i dla lewego dziecka lub prawego dziecka, jaki jest indeks nadrzędny? Dla lewego dziecka w indeksie 1 i prawym dziecku przy indeksie 2, rodzic jest w indeksie 0. Dla lewego dziecka w indeksie 3 i prawym dziecku przy indeksie 4, rodzic jest w indeksie 1. W przypadku lewego dziecka w indeksie 5 i prawym dziecku przy indeksie 6, rodzic jest w indeksie 2. Dla lewego dziecka w indeksie 7 i prawym dziecku w indeksie 8, rodzic jest w indeksie 3. Dla lewego dziecka w indeksie 9 i prawym dziecku w indeksie 10, rodzic jest w indeksie 4.
I tym razem jest indeksem dzieci (nie indeks nadrzędny). Zatem rodzic każdego lewego dziecka znajduje się na indeksie I/2 Division I/2 Division, a rodzic prawego dziecka, który jest taki sam jak rodzic lewego dziecka (rodzeństwo), jest w wyniku liczb całkowitych (I-1) /2, i.mi.:
I/2 i (I-1)/2
gdzie podział jest podziałem całkowitym.
Dobrze jest również wiedzieć, czy węzeł jest lewym dzieckiem, czy prawym dzieckiem: jeśli normalny podział przez 2 ma resztę, to jest to lewy węzeł przez indeksowanie oparte na zero. Jeśli normalny podział przez 2 nie ma pozostałej części, to jest to właściwy węzeł przez indeksowanie oparte na zero.
Kod w C, aby wiedzieć, czy węzeł dziecięcy jest lewym węzłem lub prawym węzłem, to:
if (i%2 == 0)
// Węzeł to właściwy węzeł
w przeciwnym razie
// węzeł to lewy węzeł
Po wiedzy, że węzeł jest lewym węzłem, wskaźnik nadrzędny można uzyskać jako wynik liczby całkowitej I/2. Po wiedzy, że węzeł jest prawym węzłem, wskaźnik nadrzędny można uzyskać jako wynik liczby całkowitej (i-1)/2.
Wysokość pełnej sterty binarnej i niektóre indeksy
Całkowita liczba węzłów
Zwróć uwagę z ostatniego pełnego schematu powyżej, że gdy poziom sterty wzrasta o 1, jego całkowita liczba węzłów w przybliżeniu podwaja. Dokładnie, następny poziom zawiera liczbę węzłów, które czyni nową sumę, sumę wszystkich poprzednich węzłów, plus 1, razy 2, a następnie minus 1. Gdy wysokość wynosi 1, jest 1 węzeł = (0 + 1) x 2 - 1 = 2 - 1 = 21 - 1. Gdy wysokość wynosi 2, istnieją 3 węzły = (1 + 1) x 2 - 1 = 4 - 1 = 22 - 1. Gdy wysokość wynosi 3, istnieje 7 węzłów = (3 + 1) x 2 - 1 = 8 - 1 = 23 - 1. Gdy wysokość wynosi 4, istnieje 15 węzłów = (7 + 1) x 2 - 1 = 16 - 1 = 24 - 1. Gdy wysokość wynosi 5, istnieje 31 węzłów = (15 + 1) x 2 - 1 = 32 - 1 = 25 - 1. Gdy wysokość wynosi 6, istnieje 63 węzły = (31 + 1) x 2 - 1 = 64 - 1 = 26 - 1. Gdy wysokość wynosi 7, istnieje 127 węzłów = (63 + 1) x 2 - 1 = 128 - 1 = 27 - 1; i tak dalej.
Ogólnie, gdy wysokość jest h,
NIE. węzłów = 2H - 1
Ostatni wskaźnik węzła
W przypadku drzewa binarnego i indeksowania zerowego ostatni indeks to:
Ostatni indeks = n - 1
gdzie n jest całkowitą liczbą węzłów lub po prostu liczba węzłów.
Liczba węzłów bez ostatniego rzędu
Dla pełnego drzewa binarnego dwa razy więcej węzłów dla ostatniego wiersza daje całkowitą liczbę węzłów minus 1. Widząc inny sposób, liczba węzłów dla ostatniego rzędu jest równa liczbie wszystkich poprzednich węzłów, razy dwa plus 1. Tak więc liczba węzłów bez ostatniego rzędu to:
NIE. węzłów bez ostatniego = wynik liczby całkowitej N/2
Wynika to z faktu, że dla indeksowania opartego na zerowej całkowita liczba węzłów dla pełnego drzewa binarnego jest zawsze liczbą nieparzystą. Na przykład: jeśli liczba węzłów (ogółem) wynosi 15, to 15/2 = 7½. Wynik liczby całkowitych, 7, jest pobierany, a połowa jest wyrzucana. A 7 to liczba węzłów bez ostatniego wiersza - patrz wyżej.
Indeks pierwszego węzła ostatniego wiersza
Należy znać indeks pierwszego węzła ostatniego wiersza. Dla indeksowania opartego na zero, w którym pierwszy węzeł (element) znajduje się w indeksie 0, indeks pierwszego węzła ostatniego wiersza jest liczbą węzłów dla wszystkich węzłów bez ostatniego wiersza. To jest:
Wynik całkowitej N/2
Zauważ, że w odpowiedniej tablicy węzły drzewa nazywane są elementami.
Przesiej i przesiejaj
Rozważ następujący pod-drzewo trzech węzłów:
Według minimalnej właściwości sterty węzeł nadrzędny powinien być mniejszy lub równy najmniejszemu z węzłów dzieci. Tak więc węzeł C musi zostać zamieniony na najmniejszy węzły dla dzieci; Nie ma znaczenia, czy najmniej lewe czy prawe rodzeństwo. Oznacza to, że C musi być zamienni z C, aby mieć:
Gdy „A” porusza się w górę, aby zająć miejsce C, to jest przesiewanie. Gdy C przesuwa się w dół, aby zająć miejsce, to przesiedle.
Ilustracja o rozliczaniu
Sterta, jak pokazano powyżej, jest częściowe, od najmniejszej wartości do największej wartości. To nie jest dokładne zamawianie (nie sort). Jak wyrażono powyżej, sterta może być w formie drzewa lub w formie tablicy. Jak wyrażono powyżej, aufikowanie już miało miejsce. W praktyce programista niekoniecznie znajdzie już drzewo,. Znalazłby listę, która jest całkowicie w zaburzeniu (całkowicie nieporządkowana). Ta nieuporządkowana lista może istnieć w postaci drzewa lub w postaci tablicy. Poniżej znajduje się nieuporządkowane (nieprojektowane) drzewo i odpowiadające mu tablice:
Odpowiednia tablica to:
10 | 20 | 25 | 6 | 4 | 12 | 15 | 23 | 8 | 7 | 18 | 16 | zero | zero | zero |
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
Drzewo jest odczytywane wiersz po rzędu, od lewej do prawej i od góry do dołu, podczas gdy umieszcza się w komórkach tablicy. Niech nazwa tablicy będzie ARR i niech zmienna reprezentująca indeks oparty na zero. W tym artykule root jest na indeksie 0.
To drzewo przejdzie częściowe zamówienie, aby stać się minimalnym stosem. Po zakończeniu częściowego zamówienia będzie to minimalna sterta podana w sekcji wprowadzającej tego artykułu. W tej sekcji częściowe zamówienie odbywa się ręcznie.
Aby uprościć anulowanie (proces zamawiania częściowego), pełne drzewo binarne nieorządkowane musi być pełnym drzewem binarnym przed przetworzeniem. Aby uczynić go pełnym drzewem binarnym, dodaj elementy, których wartości są większe niż najwyższa wartość znaleziona już w nieoprzestrzenionej stercie. W tym artykule zostanie to zrobione z tablicą, a nie z formą wykresu drzewa.
Najwyższy element (węzeł) to 25. Niech trzy liczby dodane, aby stworzyć pełne drzewo: 26, 27 i 28. Odpowiednia tablica pełnego drzewa staje się:
10 | 20 | 25 | 6 | 4 | 12 | 15 | 23 | 8 | 7 | 18 | 16 | 26 | 27 | 28 |
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
Kiedy lista nieo uporządkowana zostanie częściowo uporządkowana przez definicję sterty, ostatnie trzy elementy pozostaną na ich ostatnich pozycjach; i można go łatwo porzucić.
Gdy ta nie zamówiona lista jest częściowo uporządkowana według definicji sterty, byłaby to częściowo uporządkowana lista podana powyżej (wprowadzenie). Częściowo uporządkowana lista ma pewne sortowanie, ale nie jest to kompletne zamówienie (nie jest to kompletne sortowanie).
Ręczne przegrupowanie (budynek sterty)
Niezaporządkowaną tablicę można umieścić w ten sposób:
10 20 25 06 04 12 15 23 08 07 18 16 26 27 28
Jest 15 elementów. Wynik liczby całkowitych 15/2 wynosi 7. Lista należy podzielić na dwie połowy, przy czym pierwsza połowa to 7, a druga połowa, 8, w następujący sposób:
10 20 25 06 04 12 15 | 23 08 07 18 16 26 27 28 28
Ten podział można uznać za jedną główną operację. Budowanie sterty trwa od prawego końca z parami elementów, 27 i 28, zidentyfikowanych jako dzieci 15. 15 jest mniej niż 27 lub 28. Tak więc ten pod-drzewo trzech węzłów spełnia minimalną właściwość sterty, a węzły na razie nie są dotknięte. To sprawdzenie można uznać za inną główną operację. Tak więc do tej pory istnieją dwie główne operacje (jeden podział tablicy i jeden czek).
16 i 26 to dzieci 12. 12 jest mniej niż 16 lub 26. Tak więc ten pod-drzewo trzech węzłów spełnia minimalną właściwość sterty, a węzły nie są dotknięte (na razie). To sprawdzenie można uznać za inną główną operację. Tak więc do tej pory są trzy główne operacje (jedna dywizja i dwa czeki).
07 i 18 to dzieci 04. 04 jest mniej niż 07 lub 18. Tak więc ten pod-drzewo trzech węzłów spełnia minimalną właściwość sterty, a węzły nie są dotknięte (na razie). To sprawdzenie można uznać za inną główną operację. Tak więc do tej pory są cztery główne operacje (jedna dywizja i trzy kontrole).
23 i 08 to dzieci 06. 06 jest mniej niż 23 lub 08. Tak więc ten pod-drzewo trzech węzłów spełnia minimalną właściwość sterty, a węzły nie są dotknięte (na razie). To sprawdzenie można uznać za inną główną operację. Tak więc do tej pory istnieje pięć głównych operacji (jedna dywizja i cztery czeki).
Wszystkie 8 elementów ostatniego rzędu drzewa i ich rodziców zostało sprawdzonych. Aby przejść do poprzedniego poziomu, lewa część drzewa musi zostać podzielona przez 2, a wynik liczby całkowitych jest przyjmowany. Wynik liczby całkowitych 7/2 wynosi 3. Nowe umieszczenie listy to:
10 20 25 | 06 04 12 15 | 23 08 07 18 16 26 27 28 28
Ten podział można uznać za jedną główną operację. Tak więc do tej pory istnieje sześć głównych operacji (dwie dywizje i cztery kontrole).
12 i 15 to dzieci 25. 25 jest większe niż 12 lub 15. Ten pod-drzewo trzech węzłów nie spełnia minimalnej właściwości sterty, więc węzły muszą zostać dotknięte. Jednak to sprawdzanie można nadal uznać za inną główną operację. Tak więc do tej pory istnieje siedem głównych operacji (dwie dywizje i pięć kontroli).
Przesiewanie w dół musi odbyć się i być może do ostatniego rzędu. Każde przesiewanie (zamiana) jest główną operacją.
25 jest zamieniony na najmniejsze dzieci, 12, aby zapewnić konfigurację:
10 20 12 | 06 04 25 15 | 23 08 07 18 16 26 27 28 28
25 jest teraz na trzecim poziomie i nie jest już na drugim poziomie. 25 jest teraz rodzicem 16 i 26. W tym momencie 25 jest większe niż 16, ale mniej niż 26. Więc 25 i 16 są zamieniane. Ta zamiana jest kolejną główną operacją, więc do tej pory istnieje dziewięć głównych operacji (dwie dywizje, pięć czeków i dwie swapy). Nowa konfiguracja to:
10 20 12 | 06 04 16 15 | 23 08 07 18 25 26 27 28
Na drugim poziomie danej listy było ich 20 i 25. 25 zostało przesianych aż do ostatniego rzędu. 20 wciąż należy sprawdzić.
Obecnie 06 i 04 to dzieci w wieku 20 lat. 20 jest większe niż 06 lub 04. Ten pod-drzewo trzech węzłów nie spełnia minimalnej właściwości sterty, więc węzły muszą zostać dotknięte. Jednak to sprawdzanie można nadal uznać za inną główną operację. Tak więc do tej pory jest dziesięć głównych operacji (dwie dywizje, sześć czeków i dwie swapy). Przesiewanie w dół musi odbyć się i być może do ostatniego rzędu. Każde przesiewanie (zamiana) jest główną operacją.
20 jest zamieniony na najmniejsze dzieci, 04, aby dać konfigurację:
10 04 12 | 06 20 16 15 | 23 08 07 18 25 26 27 28
20 jest teraz na trzecim poziomie i nie już na drugim poziomie. 20 jest teraz rodzicem 07 i 18. W tym momencie 20 jest większe niż 07 lub 18. Więc 20 i 07 są zamieniane. Ta zamiana jest kolejną główną operacją, więc do tej pory istnieje dwanaście głównych operacji (dwie dywizje, sześć czeków i cztery swapy). Nowa konfiguracja to:
10 04 12 | 06 07 16 15 | 23 08 20 18 25 26 27 28
Przechucze w dół z poprzedniej ścieżki zakończyły się. Lewa część, która nie została całkowicie sprawdzona, musi zostać podzielona na dwa (przejście na poprzedni poziom), aby mieć:
10 | 04 12 | 06 07 16 15 | 23 08 20 18 25 26 27 28
Wynik liczby całkowitych 3/2 wynosi 1.
Obecnie 04 i 12 to dzieci 10. 10 jest większe niż 04, ale mniej niż 12. Ten pod-drzewo trzech węzłów nie spełnia minimalnej właściwości sterty, więc węzły muszą zostać dotknięte. Jednak to kontrola należy nadal uznać za kolejną główną operację. Tak więc do tej pory istnieje czternaście głównych operacji (trzy dywizje, siedem czeków i cztery swapy). Przesiewanie w dół musi odbyć się i być może do ostatniego rzędu. Każde przesiewanie (zamiana) jest główną operacją.
10 jest zamieniony na najmniejsze dzieci, 04, aby dać konfigurację:
04 | 10 12 | 06 07 16 15 | 23 08 20 18 25 26 27 28
10 jest teraz na drugim poziomie i nie jest już na pierwszym poziomie. 10 jest teraz rodzicem 06 i 07. W tym momencie 10 jest większe niż 06 lub 07. Więc 10 i 06 są zamieniane. Ta zamiana jest kolejną główną operacją, więc do tej pory istnieje szesnaście głównych operacji (trzy dywizje, siedem czeków i sześć swapów). Nowa konfiguracja to:
04 | 06 12 | 10 07 16 15 | 23 08 20 18 25 26 27 28
10 jest teraz na trzecim poziomie i nie już na drugim poziomie. 10 jest teraz rodzicem 23 i 08. W tym momencie 10 jest mniej niż 23, ale większe niż 08. Więc 10 i 08 są zamieniane. Ta zamiana jest kolejną główną operacją, więc do tej pory istnieje siedemnaście głównych operacji (trzy dywizje, siedem czeków i siedem swapów). Nowa konfiguracja to:
04 | 06 12 | 08 07 16 15 | 23 10 20 18 25 26 27 28
Cóż, sprawdzanie, podział i zamianę rozpoczęły się w ostatnim indeksie i osiągnęły pierwszy indeks, ze wszystkimi konsekwencjami przesiew w dół. Oznacza to, że budowanie sterty jest kompletne, aw tym przypadku z siedemnastoma głównymi operacjami (trzy dywizje, siedem czeków i siedem swapów). Było 15 elementów, choć trzy ostatnie były manekinem potrzebne do uproszczenia budynku sterty.
Algorytm
Istnieją różne algorytmy do budowania sterty. Podana powyżej ilustracja będzie najbardziej wydajna, jeśli wartość rodzica zostanie zamieniona na którekolwiek z dzieci, które są mniej i nie zawsze najmniej dzieci. Kroki algorytmu to:
Złożoność czasu
Złożoność czasu to względny czas wykonywania jakiegoś kodu. W takim przypadku jest to względny czas wykonywania procesu budowania sterty. Złożoność czasu to w rzeczywistości liczba głównych operacji w kodzie (program).
Oficjalnie mówi się, że złożoność czasu algorytmu tego artykułu jest N Operation. W takim przypadku N to 15. Więc złożoność czasu dla tego algorytmu wynosi 15.
Dlaczego miałby być 15 zamiast 17? To znaczy, dlaczego powinien to być n? - Cóż, ponieważ podział nie jest partycją, czas trwania każdego działań jest niewielki i można go zaniedbać. Dzięki temu i dla powyższej ilustracji liczba głównych operacji stanie się 14 (siedem czeków i siedem swapów), z 3 dywizjami ignorowanymi.
Ponadto, jeśli wartość rodzica zostanie zamieniona na którekolwiek z dzieci, które są mniejsze, a nie zawsze najmniej dzieci, ogólny czas kontroli zostanie skrócony. To sprawi, że czas sprawdzania będzie mały i tak ignorowany. Dzięki temu i dla powyższej ilustracji liczba głównych operacji stanie się 7 (siedem swapów), z 3 dywizjami, a siedem kontroli również zignorowano.
Uwaga: W przypadku dobrego programu budowania sterty tylko operacje zamiany (SIFT Downs) są rozważane w złożoności czasowej. W takim przypadku istnieje 7 operacji, a nie 15. W radzeniu sobie ze złożonością czasu maksymalna możliwa liczba operacji jest tym, co należy podać.
Możliwe jest, że wszystkie 15 węzłów powyżej zostanie zamienione. Więc złożoność czasu w tym przykładzie musi być podana jako 15, a nie 7.
Złożoność czasu dla tego algorytmu podano, ogólnie, jako:
NA)
gdzie n jest n, jest to notacja big-o. Ta notacja wykorzystuje wielkie tyłki O i jej nawiasy. Wewnątrz nawiasów znajduje się wyrażenie dla możliwej maksymalnej liczby operacji dla kodu (program) do zakończenia.
Kodowanie w c
Główną funkcją C do rozliczenia wyżej wymienionych tablicy jest:
int main (int argc, char ** argv)
int n1 = 12;
int a1 [] = 10, 20, 25, 6, 4, 12, 15, 23, 8, 7, 18, 16;
int a2 [] = 10, 20, 25, 6, 4, 12, 15, 23, 8, 7, 18, 16, 26, 27, 28;
BuildHeap (A2, N2);
for (int i = 0; i = arr [leftindx] && arr [leftindx]> = arr [righindx])
int temp = ARR [ParentIndx]; ARR [ParentIndx] = arr [rightindx]; arr [righdIndx] = temp;
lastdown = rightindx;
else if (arr [marenindx]> = arr [righdIndx] && arr [rightindx]> = arr [leftindx])
int temp = ARR [ParentIndx]; ARR [ParentIndx] = arr [leftindx]; arr [leftindx] = temp;
lastdown = leftindx;
else if (arr [marenindx]> = arr [rightIndx] && arr [rightIndx] = arr [leftindx] && arr [leftindx] <= arr[rightIndx])
int temp = ARR [ParentIndx]; ARR [ParentIndx] = arr [leftindx]; arr [leftindx] = temp;
lastdown = leftindx;
powrót Lastdown;
Istnieje funkcja przesiewania. Użyłby funkcji swap () i przesiadłby w dół do najniższego poziomu na jednej ścieżce. To jest:
int NextIndx;
void Siftdown (int arr [], int n2, int i)
int leftindx, prawy wciśnij;
Int ParentIndx = i;
leftindx = 2*i+1;
Rightindx = 2*i+2;
if (paryindx = 0)
NextIndx = Swap (ARR, ParentIndx, LeftIndx, RightIndx);
if (NextIndx = Halfindx/2; i--)
Siftdown (A2, N2, I);
N = n/2;
if (n> = 1)
BuildHeap (A2, N);
Wszystkie powyższe segmenty kodu można zmontować w celu utworzenia programu, który rozliczy nieopisaną tablicę.
Wniosek
Najbardziej wydajnym algorytmem złożoności czasowej rozlegającej się jest:
NA)