Ogólne wiadomości o grafach Algorytmy i struktury danych Wykład 5. Rok akademicki: / Pojęcie grafu Graf zbiór wierzchołków połączonych za pomocą krawędzi. Podstawowe rodzaje grafów: grafy nieskierowane, grafy skierowane.
Graf nieskierowany Graf nieskierowany G = (V, E), gdzie: V zbiór wierzchołków, E zbiór krawędzi, czyli zbiór nieuporządkowanych par wierzchołków o postaci (u, v), gdzie u, v V i u v (w grafie nieskierowanym nie mogą występowad pętle, czyli połączenie łączące wierzchołek z samym sobą). Graf nieskierowany nazywany jest grafem prostym. Pojęcia związane z grafami prostymi (nieskierowanymi) ścieżka ciąg połączonych kolejno ze sobą wierzchołków, długośd ścieżki liczba krawędzi wchodzących w skład ścieżki, ścieżka prosta jeśli wszystkie wchodzące w jej skład wierzchołki są różne (dopuszcza się jedynie, aby pierwszy i ostatni wierzchołek był identyczny) graf spójny jeśli dla każdej pary wierzchołków istnieje łącząca je ścieżka
Pojęcia związane z grafami prostymi (nieskierowanymi) krawędź (u, v) nazywana jest krawędzią przylegającą do wierzchołków u oraz v lub krawędzią incydentną z tymi wierzchołkami stopieo wierzchołka liczba incydentnych z nim krawędzi cykl ścieżka łącząca wierzchołek z samym sobą graf cykliczny graf zawierający co najmniej jeden cykl 5 Graf skierowany Graf skierowany (digraf) G = (V, E) to struktura składająca się ze zbioru wierzchołków V oraz zbioru krawędzie (zwanych także łukami). Krawędź jest uporządkowaną parą wierzchołków (u, v) u jest wierzchołkiem początkowym, a v wierzchołkiem koocowym krawędzi. 6
Graf ważony Graf ważony graf, w którym z każdą krawędzią skojarzony jest parametr numeryczny zwany wagą. Grafy ważone mogą byd grafami nieskierowanymi lub skierowanymi. 7 Reprezentacja grafów / reprezentacja za pomocą macierzy sąsiedztwa (macierzy przyległości) A C B A B C A B C 8
Reprezentacja grafów / reprezentacja listowa A B A B C B A C A C 9 Metody przeszukiwania grafu Wyróżnia się dwie podstawowe metody przeszukiwania grafu (wędrówki po grafie): DFS depth-first search przeszukiwanie wgłąb grafu BFS breadth-first search przeszukiwanie wszerz grafu 5
Przeszukiwanie wgłąb grafu. Jeśli jest to możliwe, to należy przejśd do przyległego nieodwiedzonego wierzchołka; wierzchołek ten staje się wierzchołkiem bieżącym; wierzchołek ten umieszczany jest na stosie. jeśli wykonanie kroku. nie jest możliwe usuwamy jeden element ze stosu; element znajdujący się na wierzchołku staje się elementem bieżącym. jeśli wykonanie powyższych reguł nie jest możliwe, to oznacza to koniec zadania Przeszukiwanie wgłąb grafu przykład Rezultat przeszukiwania DFS: A, B, D, F, E, C, G. lub A, E, F, B, D, C, G 6
Przeszukiwanie wszerz grafu. Jeśli jest to możliwe, to należy odwiedzid kolejny, wcześniej nieodwiedzony wierzchołek, przyległy do wierzchołka bieżącego. Odwiedzony wierzchołek umieszczany jest w kolejce. Nie następuje zmiana wierzchołka bieżącego.. Gdy nie ma już kolejnych nieodwiedzonych wierzchołków, to z kolejki pobieramy pierwszy element. Staje się on wierzchołkiem bieżącym.. Procedura kooczy swoje działania, gdy nie można zastosowad powyższych reguł brak nieodwiedzonych wierzchołków i brak elementów w kolejce. Przeszukiwanie wszerz grafu - przykład kolejność wskazywana przez numery wierzchołków 7
Algorytm Dijkstry Cel algorytmu: wyznaczanie najkrótszej drogi prowadzącej z rozpatrywanego wierzchołka do każdego innego Autor: Edsger Dijkstra, 959 5 Podstawowe założenia algorytmu działanie algorytmu rozpoczyna się od wskazania wierzchołka początkowego w kolejnych krokach przetwarzane są kolejne wierzchołki. Rozpatrywane są dwa zbiory wierzchołków: S wierzchołki przetworzone V wierzchołki nieprzetworzone algorytm korzysta z dwóch wektorów: d[i] długośd drogi od wierzchołka początkowego do i-tego wierzchołka p[i] informacja o najkrótszej ścieżce (indeks wierzchołka bezpośrednio poprzedzającego i-ty wierzchołek na najkrótszej ścieżce) 6 8
Algorytm Dijkstry Inicjalizacja obliczeo Element bieżący: Spośród elementów zbioru V wyszukiwany jest ten, do którego przejście związane jest z minimalnym kosztem (dla v = ). Element ten przesuwany jest do zbioru S. V d(,v) p(v) S/V S V V V V 7 Algorytm Dijkstry Bieżący węzeł: Koszt dotarcia do bieżącego węzła: jeżeli koszt dotarcia do bieżącego węzła + koszt dotarcia od bież. do v-tego < dotychczas określony koszt dotarcia do v-tego węzła to modyfikacja d(v) Wybór kolejnego el. o min. koszcie () V d(v) p(v) S/V S V S 6 V 5 V 8 9
Algorytm Dijkstry Bieżący węzeł: Koszt dotarcia do bieżącego węzła: 5 jeżeli koszt dotarcia do bieżącego węzła + koszt dotarcia od bież. do v-tego < dotychczas określony koszt dotarcia do v-tego węzła to modyfikacja d(v) Wybór kolejnego el. o min. koszcie () V d(v) p(v) S/V S V S 6 V 5 S 9 Algorytm Dijkstry Bieżący węzeł: Koszt dotarcia do bieżącego węzła: 6 jeżeli koszt dotarcia do bieżącego węzła + koszt dotarcia od bież. do v-tego < dotychczas określony koszt dotarcia do v-tego węzła to modyfikacja d(v) Wybór kolejnego el. o min. koszcie () V d(v) p(v) S/V S 7 V S 6 S 5 S
Algorytm Dijkstry Wyniki koocowe: -: koszt: 7 (---) -: koszt: (-) -: koszt: 6 (--) -: koszt: 5 (--) V d(v) p(v) S/V S 7 S S 6 S 5 S Zastosowanie algorytmu Dijkstry Open Shortest Path First (OSPF) protokół routingu wewnętrznego wykorzystywany w sieci Internet (obok protokołu RIP). Wyznaczanie optymalnych tras przesyłania pakietów realizowane jest przez routery.
Minimalne drzewo rozpinające Drzewem rozpinające grafu G nazywamy drzewo, które zawiera wszystkie wierzchołki grafu G, zaś zbiór krawędzi drzewa jest podzbiorem zbioru krawędzi grafu. Minimalne drzewo rozpinające drzewo rozpinające w grafie ważonym, dla którego suma wag jest najmniejsza z możliwych koszt: 5 koszt: Algorytm Prima Algorytm wyznaczania minimalnego drzewa rozpinającego R. C. Prim, 957
Podstawowe założenia algorytmu działanie algorytmu rozpoczyna się od wskazania wierzchołka początkowego w kolejnych krokach przetwarzane są kolejne wierzchołki. Rozpatrywane są dwa zbiory wierzchołków: S wierzchołki przetworzone V wierzchołki nieprzetworzone algorytm korzysta z dwóch wektorów: d[i] długośd drogi od wierzchołka bieżącego do i-tego wierzchołka p[i] informacja o kolejnej krawędzi dodawanej do minimalnego drzewa rozpinającego (indeks wierzchołka bezpośrednio poprzedzającego i-ty wierzchołek) 5 Algorytm Prima Inicjalizacja obliczeo Spośród elementów zbioru V wyszukiwany jest ten, do którego przejście związane jest z minimalnym kosztem (dla v = ). Element ten przesuwany jest do zbioru S. V d(,v) p(v) S/V S V V V V 6
Algorytm Prima Bieżący węzeł: jeżeli koszt dotarcia od bież. do v-tego < dotychczas określony koszt dotarcia do v-tego węzła to modyfikacja d(v) Wybór kolejnego el. o min. koszcie () V d(v) p(v) S/V S V S V V 7 Algorytm Prima Bieżący węzeł: jeżeli koszt dotarcia od bież. do v-tego < dotychczas określony koszt dotarcia do v-tego węzła to modyfikacja d(v) Wybór kolejnego el. o min. koszcie () V d(v) p(v) S/V S V S V S 8
Algorytm Prima Bieżący węzeł: Koszt dotarcia do bieżącego węzła: 6 jeżeli koszt dotarcia od bież. do v-tego < dotychczas określony koszt dotarcia do v-tego węzła to modyfikacja d(v) Wybór kolejnego el. o min. koszcie () V d(v) p(v) S/V S V S S S 9 Algorytm Prima Wyniki koocowe: Zbiór krawędzi: Koszt drzewa: V d(v) p(v) S/V S S S S S 5
Implementacja drzew i grafów Biblioteka JDSL www.jdsl.org 6
Przykładowy program import jdsl.core.api.*; import jdsl.core.ref.*; public class Tree { //puste drzewo static Tree pustedrzewo () { Tree t = new NodeTree(); return t; //drzewo - tylko korzen static Tree tylkokorzen () { Tree t = new NodeTree(); t.replaceelement(t.root(),"korzeo"); return t;... //drzewo reprezentujace dni tygodnia static Tree dnitygodnia () { Tree t = new NodeTree(); t.replaceelement(t.root(),"tydzieo"); t.insertlastchild(t.root(),"poniedziałek"); t.insertlastchild(t.root(),"wtorek"); t.insertlastchild(t.root(),"środa"); t.insertlastchild(t.root(),"czwartek"); t.insertlastchild(t.root(),"piątek"); t.insertlastchild(t.root(),"sobota"); t.insertlastchild(t.root(),"niedziela"); return t; 7
... static Tree rok() { Tree t = new NodeTree(); Position p; t.replaceelement(t.root(),"rok"); p = t.insertlastchild(t.root(),"zima"); t.insertlastchild(p,"styczeo"); t.insertlastchild(p,"luty"); t.insertlastchild(p,"marzec"); p = t.insertlastchild(t.root(),"wiosna"); t.insertlastchild(p,"kwiecieo"); t.insertlastchild(p,"maj"); t.insertlastchild(p,"czerwiec"); 5... p = t.insertlastchild(t.root(),"lato"); t.insertlastchild(p,"lipiec"); t.insertlastchild(p,"sierpieo"); t.insertlastchild(p,"wrzesieo"); p = t.insertlastchild(t.root(),"jesieo"); t.insertlastchild(p,"październik"); t.insertlastchild(p,"listopad"); t.insertlastchild(p,"grudzieo"); return t; public static void main(string [] args) {... // koniec programu 6 8
Poruszanie się po drzewie sposób poruszania się po drzewie kolejnośd odwiedzania węzłów trzy podstawowe metody poruszania się po drzewie: preorder, postorder, inorder. 7 Tworzenie przykładowego drzewa static Tree liczby() { Tree t = new NodeTree(); Position p, r; t.replaceelement(t.root(),new Integer()); t.insertlastchild(t.root(), new Integer()); 5 6 7 p = t.insertlastchild(t.root(), new Integer()); r = t.insertlastchild(p, new Integer(5)); t.insertlastchild(r, new Integer(8)); t.insertlastchild(r, new Integer(9)); 8 9 r = t.insertlastchild(p, new Integer(6)); t.insertlastchild(r, new Integer()); p = t.insertlastchild(t.root(), new Integer()); t.insertlastchild(p, new Integer(7)); return t; 8 9
Metoda preorder / 5 6 7 8 9 Metoda preorder - rozpoczyna się od korzenia drzewa, a następnie odwiedzane są wszystkie jego poddrzewa w kolejności od lewej do prawej strony Przechodzenie preorder: 5 8 9 6 7 9 Metoda preorder / static void preorder(tree t, Position p) { System.out.print(p.element() + " "); try { p = t.firstchild(p); catch (Exception e) {p = null; while (p!= null) { preorder(t, p); try { p = t.siblingafter(p); catch (Exception e) {p = null;
Metoda postorder / 5 6 7 8 9 Metoda postorder - w pierwszej kolejności odwiedzane są wszystkie poddrzewa w kolejności od lewej do prawej strony, a następnie odwiedzany jest korzeo drzewa. Przechodzenie postorder: 8 9 5 6 7 Metoda postorder / static void postorder(tree t, Position p) { Position p = p; try {p = t.firstchild(p); catch (Exception e) {p = null; while (p!= null) { postorder(t, p); try {p = t.siblingafter(p); catch (Exception e) {p = null; System.out.print(p.element() + " ");
Metoda inorder / 5 6 7 8 9 Metoda inorder - odwiedzane jest lewe skrajne poddrzewo, następnie korzeo drzewa, po czym następuje przejście przez pozostałe poddrzewa w kolejności od lewej do prawej strony. Przechodzenie inorder: 8 5 9 6 7 Metoda inorder / static void inorder(tree t, Position p) { Position p = p; try { if (t.isexternal(p)) System.out.print(p.element() + " "); else { try { p = t.firstchild(p); inorder(t, p); catch (Exception e) { System.out.print(p.element() + " "); try {p = t.siblingafter(p); catch (Exception e) {p = null;
Metoda inorder / while (p!= null) { inorder(t, p); try {p = t.siblingafter(p); catch (Exception e) {p = null; catch (InvalidAccessorException e) { 5 Implementacja grafów Klasa IncidenceListGraph dla każdego wierzchołka przechowywana jest lista wierzchołków przyległych (reprezentacja listowa) A B A B C B A C A C 6
Zastosowanie algorytmu Dijkstry import jdsl.graph.api.*; import jdsl.graph.algo.integerdijkstrapathfinder; import jdsl.graph.ref.*; public class Graph { public static void main(string[] args) { Graph gr = new IncidenceListGraph(); Vertex v[] = new Vertex[5]; //tworzenie i wstawianie wierzcholkow for (int i = ; i < v.length; i++) v[i] = gr.insertvertex(integer.tostring(i)); 7... //wstawianie krawędzi gr.insertdirectededge(v[],v[],new Integer()); gr.insertdirectededge(v[],v[],new Integer()); gr.insertdirectededge(v[],v[],new Integer()); gr.insertdirectededge(v[],v[],new Integer()); gr.insertdirectededge(v[],v[],new Integer()); gr.insertdirectededge(v[],v[],new Integer()); gr.insertdirectededge(v[],v[],new Integer()); gr.insertdirectededge(v[],v[],new Integer()); gr.insertdirectededge(v[],v[],new Integer()); gr.insertdirectededge(v[],v[],new Integer()); 8
... IntegerDijkstraPathfinder dist = new IntegerDijkstraPathfinder() { protected int weight(edge e) { Integer i = (Integer) e.element(); return i.intvalue(); ; dist.execute(gr,v[],v[]); EdgeIterator ei = dist.reportpath(); 9... int sum = ; while(ei.hasnext()) { Edge e = ei.nextedge(); sum += ((Integer) e.element()).intvalue(); System.out.println(gr.origin(e) + " --> " + gr.destination(e)); System.out.println(sum); 5 5