Konstrukcja drzew binarnych

Jak wiemy, obiekty ze wskaźnikiem do innego obiektu tego samego typu można łączyć w strukturę danych zwaną listą (zagadnienie to zostało omówione w e‑materiale Podstawowe struktury danych: listaPuhk7mD92Podstawowe struktury danych: lista).

Jeśli w klasie obiektu zdefiniujemy dwa wskaźniki do obiektów tego samego typu, to otrzymamy struktury bardziej złożone listy jednokierunkowelista jednokierunkowalisty jednokierunkowe.

W programowaniu w języku Java drzewo poszukiwań binarnychdrzewo binarnedrzewo poszukiwań binarnych realizuje się jako strukturę danych z dowiązaniami, w której każdy węzeł będzie obiektem o trzech polach:

  1. klucz – klucz węzła; przechowuje określoną wartość, np.  typu stałoprzecinkowego;

  2. lewy – referencja do lewego syna;

  3. prawy – referencja do prawego syna.

R164iPX4pQL8n
Konstrukcja drzewa binarnego
Źródło: Contentplus.pl Sp. z o.o., licencja: CC BY-SA 3.0.

Klasą obiektu węzła będzie w naszej implementacji klasa Wezel. Do konstruktora klasy przekażemy tylko wartość klucza klucz typu int. Domyślnie pola lewyprawy będą miały wartość null, co wskazuje na brak połączenia.

Linia 1. public class Wezel otwórz nawias klamrowy. Linia 2. int klucz średnik. Linia 3. Wezel lewy przecinek prawy średnik. Linia 5. public Wezel otwórz nawias okrągły int klucz zamknij nawias okrągły otwórz nawias klamrowy. Linia 6. this kropka klucz znak równości klucz średnik. Linia 7. this kropka lewy znak równości null średnik. Linia 8. this kropka prawy znak równości null średnik. Linia 9. zamknij nawias klamrowy. Linia 10. zamknij nawias klamrowy.

Wszystkie odwołania do obiektów w języku Java są referencjami i można ich używać jak wskaźników. Zatem w celu dodania pewnego węzła do drzewa binarnego, należy przypisać referencję do węzła, który chcemy dodać. Wykonujemy to w polu lewy lub prawy już istniejącego węzła.

Linia 1. public class DrzewoBinarne otwórz nawias klamrowy. Linia 2. prawy ukośnik asterysk kropka kropka kropka asterysk prawy ukośnik. Linia 3. public static void main otwórz nawias okrągły String otwórz nawias kwadratowy zamknij nawias kwadratowy args zamknij nawias okrągły otwórz nawias klamrowy. Linia 4. Wezel korzen znak równości new Wezel otwórz nawias okrągły 6 zamknij nawias okrągły średnik. Linia 5. korzen kropka lewy znak równości new Wezel otwórz nawias okrągły 4 zamknij nawias okrągły średnik. Linia 6. korzen kropka lewy kropka lewy znak równości new Wezel otwórz nawias okrągły 11 zamknij nawias okrągły średnik. Linia 7. korzen kropka prawy znak równości new Wezel otwórz nawias okrągły 20 zamknij nawias okrągły średnik. Linia 8. korzen kropka prawy kropka prawy znak równości new Wezel otwórz nawias okrągły 22 zamknij nawias okrągły średnik. Linia 9. zamknij nawias klamrowy. Linia 10. zamknij nawias klamrowy.

By zdefiniować drzewo binarne w kodzie, zwykle stosuje się jedną zmienną korzen będącą referencją do korzenia drzewa. Z tej zmiennej możemy się dostać do dowolnego węzła w drzewie binarnym poprzez ciąg odwołań do prawych i lewych synów.

Operacje będziemy wykonywać na referencjach obiektów klasy Wezel. Aby uzyskać dostęp do pól obiektu z poziomu referencji, używamy notacji kropkowej.

Linia 1. Wezel wezel znak równości new Wezel otwórz nawias okrągły 5 zamknij nawias okrągły średnik. Linia 2. System kropka out kropka println otwórz nawias okrągły wezel kropka klucz zamknij nawias okrągły średnik prawy ukośnik prawy ukośnik wynik znak równości 5.

Aby stworzyć reprezentację drzewa, które zostało zaprezentowane na ilustracji na początku e‑materiału, użyjemy klasy Wezel, utworzymy jej instancje dla każdego węzła i przypiszemy im odpowiednio lewe i prawe poddrzewa.

Implementacja drzewa binarnego zaprezentowanego na początku sekcji:

Linia 1. public class Wezel otwórz nawias klamrowy. Linia 2. int klucz średnik. Linia 3. Wezel lewy przecinek prawy średnik. Linia 5. public Wezel otwórz nawias okrągły int klucz zamknij nawias okrągły otwórz nawias klamrowy. Linia 6. this kropka klucz znak równości klucz średnik. Linia 7. this kropka lewy znak równości null średnik. Linia 8. this kropka prawy znak równości null średnik. Linia 9. zamknij nawias klamrowy. Linia 11. public static void main otwórz nawias okrągły String otwórz nawias kwadratowy zamknij nawias kwadratowy args zamknij nawias okrągły otwórz nawias klamrowy. Linia 12. Wezel korzen znak równości new Wezel otwórz nawias okrągły 6 zamknij nawias okrągły średnik. Linia 13. korzen kropka lewy znak równości new Wezel otwórz nawias okrągły 4 zamknij nawias okrągły średnik. Linia 14. korzen kropka lewy kropka lewy znak równości new Wezel otwórz nawias okrągły 11 zamknij nawias okrągły średnik. Linia 15. korzen kropka prawy znak równości new Wezel otwórz nawias okrągły 20 zamknij nawias okrągły średnik. Linia 16. korzen kropka prawy kropka prawy znak równości new Wezel otwórz nawias okrągły 22 zamknij nawias okrągły średnik. Linia 17. zamknij nawias klamrowy. Linia 18. zamknij nawias klamrowy.

W zaprezentowanym kodzie tworzymy nowe węzły i przypisujemy im odpowiednio wartości klucza. Przypisujemy także węzły lewyprawy do każdego utworzonego węzła, aby odtworzyć strukturę drzewa z ilustracji.

Słownik

drzewo
drzewo

struktura danych, która reprezentuje drzewo matematyczne, w której występują wierzchołki, krawędzie, liście i korzeń

drzewo binarne
drzewo binarne

drzewo ukorzenione, w którym każdy węzeł ma najwyżej dwóch synów

korzeń
korzeń

węzeł (element) w drzewie, który nie ma żadnego rodzica; pierwszy element drzewa

lista jednokierunkowa
lista jednokierunkowa

dynamiczna struktura danych, która ma przechowywać nieznane z góry ilości danych tego samego typu; zbudowana jest z węzłów, z których każdy składa się z pojedynczego elementu przechowywanego w liście oraz wskaźnika do kolejnego elementu listy

liść
liść

węzeł (element) w drzewie, który nie ma żadnych następników (potomków); często liśćmi są węzły najbardziej oddalone od korzenia

wyciek pamięci (ang. memory leak)
wyciek pamięci (ang. memory leak)

część programu, która wykorzystuje zarezerwowaną pamięć bez jej ostatecznego zwalniania