Document

advertisement
Podstawowe pojęcia dotyczące drzew
Podstawowe pojęcia dotyczące grafów
Przykłady drzew i grafów
Drzewa:
Drzewo (ang. tree) jest strukturą danych zbudowaną z elementów,
które nazywamy węzłami (ang. node). Dane przechowuje się w
węzłach drzewa. Węzły są ze sobą powiązane w sposób
hierarchiczny za pomocą krawędzi (ang. edge), które zwykle
przedstawia się za pomocą strzałki określającej hierarchię.
Pierwszy węzeł drzewa nazywa się korzeniem (ang. root node).
Od niego "wyrastają" pozostałe węzły, które będziemy nazywać
synami (ang. child node). Synowie są węzłami podrzędnymi w
strukturze hierarchicznej. Synowie tego samego ojca są nazywani
braćmi (ang. sibling node). Węzeł nadrzędny w stosunku do syna
nazwiemy ojcem (ang. parent node). Ojcowie są węzłami
nadrzędnymi w strukturze hierarchicznej. Jeśli węzeł nie posiada
synów, to nazywa się liściem (ang. leaf node), w przeciwnym
razie nazywa się węzłem wewnętrznym (ang. internal node,
inner node, branch node).
Za wyjątkiem korzenia wszystkie pozostałe węzły w drzewie posiadają swojego ojca. W
normalnym drzewie liczba synów dla dowolnego węzła nie jest ograniczona. Istnieje
jednakże bardzo ważna klasa drzew, w których dany węzeł może posiadać co najwyżej
dwóch synów. Noszą one nazwę drzew binarnych (ang. binary tree).
Ciąg węzłów połączonych krawędziami nazwiemy ścieżką (ang. path). Od korzenia do
określonego węzła w drzewie wiedzie zawsze dokładnie jedna ścieżka prosta, tzn. taka, iż
zawarte w niej węzły pojawiają się tylko jeden raz. Długością ścieżki (ang. path
length) nazwiemy liczbę krawędzi łączących węzły w ścieżce. Dla naszego drzewa mamy
następujące ścieżki proste od korzenia do kolejnych węzłów:
Długość ścieżki prostej od korzenia do danego węzła nazywa się poziomem węzła (ang.
node level). Korzeń drzewa ma zawsze poziom 0. W naszym drzewie węzły B, C i D mają
poziom 1, a E, F, G i H mają poziom 2.Wysokość drzewa (ang. tree height) jest równa
największemu poziomowi węzłów (lub najdłuższej ścieżce rozpoczynającej się w
korzeniu).
Dla naszego drzewa wysokość jest równa 2. Wysokość węzła (ang. node height), to długość
najdłuższej ścieżki od tego węzła do liścia. Dla korzenia wysokość węzła jest równa
wysokości drzewa:
Poziom drzewa (ang. tree level, the
level of a tree) dla danego węzła to
długość ścieżki prostej od korzenia do
danego węzła.
Liczba krawędzi powiązanych z danym węzłem nosi nazwę stopnia węzła (ang.
node degree). Krawędzie drzewa są krawędziami skierowanymi (ang. directed
edge) i oznaczamy je za pomocą strzałek. Kierunek strzałki jednoznacznie
określa pozycję w hierarchii – strzałka wychodzi od ojca i kończy się na synu. Z
tego powodu stopień węzła rozbija się na dwa stopnie:
stopień wejściowy (ang. node in-degree) – liczba krawędzi wchodzących do
węzła, dla drzewa nigdy nie przekracza 1, a jest równy 0 tylko dla korzenia.
stopień wyjściowy (ang. node out-degree) – liczba krawędzi wychodzących
z węzła, określa liczbę synów.
Stopień węzła jest sumą stopnia
wejściowego i wyjściowego.
Zwróć uwagę, że liście nie będące korzeniem (jeśli korzeń
jest liściem, to jego stopień wynosi 0) mają zawsze
stopień równy 1.
Drzewo binarne:
Drzewo, w którym węzły mogą posiadać co najwyżej
dwóch synów, nazywa się drzewem binarnym (ang.
binary tree, B-tree). Węzły potomne nazywamy
odpowiednio synem lewym(ang. left child
node) i synem prawym (ang. right child node).
Drzewa binarne mają ogromne znaczenie w
informatyce, ponieważ za ich pomocą można
odwzorować również drzewa, których węzły posiadają
dowolną liczbę synów – sposób takiego odwzorowania
podamy w dalszej części rozdziału.
Regularne drzewo binarne (ang. regular binary tree, proper binary tree) zawiera
wyłącznie węzły, których stopień wyjściowy jest albo równy 2 (węzeł posiada dwóch
synów – jest węzłem wewnętrznym), albo 0 (węzeł nie posiada synów – jest liściem).
Dla regularnego drzewa binarnego liczba węzłów na poziomie k-tym jest
zawsze równa 2k. Liczba wszystkich węzłów, czyli rozmiar drzewa (ang. binary
tree size) jest równa 2p - 1, gdzie p oznacza liczbę poziomów.
Dla n węzłów liczba poziomów jest równa
log2(n+1).
Ponumerujmy poziomami kolejne
węzły, idąc od strony lewej do prawej:
Otrzymane numery węzłów są powiązane ze
strukturą hierarchii drzewa prostymi
zależnościami:
Węzeł o numerze k znajduje się na poziomie o numerze [log2(k+1)].
Węzeł o numerze k jest wewnętrzny, jeśli 2k+2 < n. W przeciwnym razie
węzeł jest liściem.
Własności te pozwalają odwzorowywać regularne drzewo binarne w ciąg
elementów i na odwrót
Kompletne drzewo binarne (ang. complete binary
tree) posiada zapełnione węzłami wszystkie poziomy z
wyjątkiem ostatniego, jednakże na ostatnim poziomie węzły
są zapełnione począwszy od lewej strony.
Kompletne drzewo binarne również da się odwzorować w ciąg węzłów. W
takim drzewie liczba elementów n może być mniejsza od maksymalnej liczby
węzłów, ponieważ ostatni poziom nie musi posiadać kompletu węzłów.
Jednakże w przeciwieństwie do drzewa regularnego węzeł wewnętrzny może
posiadać tylko jednego, lewego syna (u nas węzłem takim jest węzeł 4).
Dlatego w kompletnym drzewie binarnym o rozmiarze n dla węzła o
numerze k zachodzi:
2k + 2 > n – węzeł jest liściem
2k + 2 = n – węzeł jest ostatnim węzłem wewnętrznym i posiada tylko
lewego syna
2k + 2 < n – węzeł jest węzłem wewnętrznym i posiada obu synów.
Poddrzewo (ang. subtree) jest drzewem zawartym w drzewie, gdy jako
korzeń przyjmiemy jeden z węzłów. Dla danego węzła drzewa
binarnego mogą istnieć dwa poddrzewa: lewe poddrzewo (ang. left
subtree) – korzeniem jest lewy syn i analogicznie prawe
poddrzewo (ang. right subtree) – korzeniem jest prawy syn:
Reprezentacja drzew binarnych w
programie:
Istnieje wiele różnych rozwiązań dla reprezentacji drzew
binarnych w pamięci komputera. Tutaj podamy te
najprostsze.
Kompletne drzewo binarne:
W tym przypadku drzewo możemy odwzorować w
tablicy n-elementowej. Każdy element tablicy jest
węzłem. Hierarchię drzewa przedstawiamy przy
pomocy indeksów i ich własności dla kompletnych
drzew binarnych. Korzeniem drzewa jest element o
indeksie 0. Jego dwoma synami są kolejno elementy o
indeksach 1(lewy syn) i 2 (prawy syn). Postępując
podobnie z pozostałymi węzłami otrzymamy całe
drzewo binarne:
Niekompletne drzewo binarne
Drzewo odwzorowujemy podobnie jak listę. Każdy element jest strukturą,
która oprócz danych zawiera dwa lub trzy wskaźniki:
Gdzie:
up – wskazuje ojca danego węzła. Dla korzenia pole to zawiera
wskazanie puste
left – wskazuje lewego syna
right – wskazuje prawego syna
data – dane dowolnego typu przechowywane przez węzeł
Wskaźniki pozwalają na przemieszczanie się po węzłach w strukturze
drzewa. Wskaźniki left i right umożliwiają przechodzenie w dół drzewa.
Wskaźnik upprowadzi w górę do ojca danego węzła. Jeśli ten kierunek
nie jest istotny, to wskaźnik może zostać pominięty (wersja
uproszczona).
Reprezentacja drzew dowolnych:
Drzewo dowolne może posiadać węzły o dowolnej liczbie
synów. Jeśli liczba możliwych węzłów potomnych nie
jest duża, to do reprezentacji takiego drzewa można
wykorzystać metodę z drzewa binarnego, zwiększając
odpowiednio liczbę wskaźników. Na przykład
dla drzew czwórkowych (ang. quadtree) możemy
zaimplementować następującą strukturę danych:
Gdy liczba synów jest duża, to rezerwowanie w każdym węźle
pól na wskaźniki przestaje być efektywne. Zamiast prostych pól
możemy umieścić w każdym węźle tablicę dynamiczną o
wymaganym rozmiarze, której każdy element jest wskaźnikiem
do syna danego węzła. Do obsługi takiej struktury będzie
potrzebna jeszcze informacja o liczbie elementów w tablicy.
Dodatkowo musimy pamiętać o zwolnieniu tablic
dynamicznych, gdy drzewo jest usuwane z pamięci.
Alternatywnym rozwiązaniem jest zastosowanie listy jednokierunkowej, której elementy
przechowują wskazania synów danego węzła. Wymaga to dołączenia do programu metod
obsługi takiej listy, a najlepiej zastosowanie odpowiedniego obiektu.
Zwróć uwagę, że w powyższym rozwiązaniu występują tzw. odwołania krzyżowe. Polegają
one na tym, iż element jednej struktury odwołuje się do innej struktury, która z kolei
odwołuje sie do tej pierwszej. Elementy listy zawierają wskazania węzłów drzewa w
polu node. Z kolei węzły drzewa w polu child wskazują listę. Obie struktury odwołują się
do siebie nawzajem. Konieczne jest utworzenie pomocniczego typu danych
PAnyTreeNode i użycie go w definicji listy. Nie można tutaj zastosować typu
AnyTreeNode, ponieważ nie jest on w tym miejscu jeszcze zdefiniowany. Natomiast typ
pomocniczy PAnyTreeNode informuje kompilator, że właściwa definicja zostanie podane
później w programie.
Grafy:
Graf (ang. graph) jest strukturą danych składającą się z dwóch
zbiorów: zbioru wierzchołków (ang. vertices) i
zbioru krawędzi (ang. edges), co matematycznie zapisujemy w
postaci uporządkowanej pary (tzn. takiej, gdzie istotna jest
kolejność elementów tworzących tę parę):
G = (V, E)
V = { v1, v2,...,vn } – zbiór n ponumerowanych
wierzchołków (ang. V = Vertex)
E = { e1, e2, ... em } – zbiór m ponumerowanych
krawędzi (ang. E = Edge). Każda krawędź jest parą (w grafie
skierowanym parą uporządkowaną) wierzchołków grafu
połączonych tą krawędzią: E = {(u,v): u,v V}
Rząd grafu (ang. graph order) to liczba wierzchołków w
grafie.
Rozmiar grafu (ang. graph size) to liczba krawędzi w grafie.
Wierzchołki grafu przechowują informację, natomiast
krawędzie określają sposób poruszania się po grafie: z
wierzchołka u można przejść do wierzchołka v tylko wtedy,
gdy istnieje ścieżka (ciąg krawędzi), która prowadzi w grafie
od wierzchołka u do v.
Grafem zerowym (ang. null graph) jest graf, który posiada
wierzchołki, lecz nie posiada żadnych krawędzi:
Wierzchołek nie połączony krawędzią z żadnym innym
wierzchołkiem grafu nazywamy wierzchołkiem izolowanym (ang.
isolated vertex):
Dane dwa wierzchołki mogą być połączone ze sobą za pomocą więcej niż jednej
krawędzi, które nazywamy krawędzią wielokrotną (ang. multi-edge). Również
wierzchołek może łączyć się krawędzią z samym sobą. Otrzymujemy wtedy
tzw. pętlę (ang. loop). Graf zawierający pętle lub krawędzie wielokrotne
nazywamy multigrafem (ang. multigraph).
Graf nie posiadający pętli oraz krawędzi podwójnych
nazywamy grafem prostym (ang. simple graph lub strict graph).
Krawędź, którą można przebywać tylko w określoną stronę, nazywa
się krawędzią skierowaną (ang. directed edge). Na rysunku
krawędzie skierowane oznaczamy strzałkami. Graf zawierający
krawędzie skierowane nazywamy grafem skierowanym (ang.
directed graph) lub w skrócie digrafem (ang. digraph). Graf nie
posiadający krawędzi skierowanych nazywamy grafem
nieskierowanym (ang. not directed graph). W definicji digrafu
zbiór krawędzi tworzą uporządkowane pary wierzchołków (u,v), z
których u oznacza wierzchołek początkowy krawędzi, a v
wierzchołek końcowy. Krawędź nieskierowana może być
przedstawiona jako dwie krawędzie skierowane w przeciwnych
kierunkach.
Zwróć uwagę, że w grafie skierowanym mogą istnieć wierzchołki,
pomiędzy którymi nie da się przejść, pomimo istnienia łączących
je krawędzi. Oto najprostszy przykład:
W grafie tym istnieje droga od wierzchołka v1 do v2, jednakże nie ma drogi
powrotnej od v2 do v1, ponieważ łącząca te wierzchołki krawędź może być
przebyta tylko w jednym kierunku.
Z krawędziami grafu mogą być związane dodatkowe wartości (np. w świecie
rzeczywistym pokonanie drogi z jednego miasta do drugiego może wymagać
określonej ilości czasu lub energii). Wartości te nazywamy wagami (ang. weight), a
graf posiadający takie krawędzie nazywamy grafem ważonym (ang. weighted graph).
Graf ważony posiada zbiór krawędzi zbudowany z uporządkowanych trójek, gdzie dwa
pierwsze elementy określają wierzchołki połączone daną krawędzią (w grafie
skierowanym wierzchołki te są parą uporządkowaną), a trzeci element określa wagę tej
krawędzi.
Stopniem wierzchołka (ang. degree) nazywamy liczbę krawędzi, które
łączą się z danym wierzchołkiem. Jeśli graf posiada pętle, to liczymy je za 2.
W poniższym grafie wierzchołki posiadają następujące stopnie:
W grafie skierowanym rozróżniamy stopień wchodzący (ang. indegree) – liczba
krawędzi wchodzących do wierzchołka oraz stopień wychodzący (ang. outdegree) –
liczba krawędzi wychodzących z wierzchołka.
Wierzchołek izolowany posiada zawsze stopień 0.
Ścieżka lub droga (ang. path) jest uporządkowanym ciągiem kolejnych krawędzi, po
których należy przejść, aby dotrzeć z wierzchołka startowego (ang. start
vertex) do wierzchołka końcowego (ang. end vertex). W grafie może istnieć wiele
różnych ścieżek pomiędzy dwoma wybranymi wierzchołkami.
Ścieżki można również definiować za pomocą ciągu kolejno
mijanych wierzchołków (oczywistym jest, iż każde dwa kolejne
wierzchołki muszą być połączone krawędzią).
Najkrótsza ścieżka (ang. shortest path) to ta, która zawiera najmniej
krawędzi/wierzchołków. Długość ścieżki (ang. path length) to liczba zawartych w
niej krawędzi/wierzchołków.
Mówimy, że ścieżka jest prosta (ang. strait path lub simple path), jeśli każdą
krawędź/wierzchołek przechodzimy tylko jeden raz.
Cykl (ang. cycle) to ścieżka, która
rozpoczyna się i kończy w tym samym
wierzchołku. Uwaga: nie myl cyklu z pętlą –
pętla to pojedyncza krawędź.
Cykl nazywamy prostym (ang. simple cycle), jeśli każda jego
krawędź/wierzchołek jest przechodzona dokładnie jeden raz. Nie odnosi się
to oczywiście do wierzchołka startowego i końcowego, które w cyklu muszą
być tym samym wierzchołkiem, innymi słowy ścieżka musi być zamknięta.
Ścieżka prosta zawierająca wszystkie wierzchołki grafu nosi nazwę ścieżki
Hamiltona (ang. Hamiltonian path).
Cykl prosty zawierający wszystkie wierzchołki grafu nazywa się cyklem
Hamiltona (ang. Hamiltonian cycle).
Ścieżka prosta, która przechodzi przez wszystkie krawędzie grafu nazywa
się ścieżką Eulera (ang. Eulerian path).
Cykl Eulera (ang. Eulerian cycle) to cykl prosty, który przechodzi przez
wszystkie krawędzie grafu. Zwróć uwagę, że cykl Eulera i cykl Hamiltona to
nie to samo! W cyklu Hamiltona ważne jest przejście przez wszystkie
wierzchołki dokładnie jeden raz (niektóre krawędzie grafu mogą być w ogóle
nie przechodzone). W cyklu Eulera z kolei musimy przejść przez każdą
krawędź, zatem niektóre wierzchołki mogą zostać kilkakrotnie odwiedzone,
jeśli łączą się z kilkoma krawędziami.
Graf nazywamy acyklicznym (ang. acyclic graph), jeśli nie posiada żadnych
cykli.
Graf nazywamy planarnym (ang. planar graph), jeśli da się go narysować na
płaszczyźnie tak, aby żadne jego krawędzie się nie przecinały.
Download