C - Wkuwanko.pl
Transkrypt
C - Wkuwanko.pl
Podstawy Informatyki • Struktury danych – cz. II • Języki programowania • Wybrane metody rozwiązywania zadań algorytmicznych Drzewa Drzewo jest hierarchicznym ułożeniem danych. DEF. Drzewo jest to zbiór T jednego lub więcej elementów zwanych węzłami, takich że: 1. istnieje jeden wyróżniony węzeł zwany korzeniem drzewa 2. pozostałe węzły (bez korzenia) są podzielone na m ≥ 0 rozłącznych zbiorów T1, .., Tm, z których każdy jest drzewem. Drzewa T1, .., Tm są nazywane poddrzewami korzenia. Pierwszy obiekt zwany jest korzeniem, kolejne obiekty traktowane są jako jego potomstwo: węzły, liście – węzły nie mające potomstwa. Droga w drzewie – sekwencja węzłów w drzewie odpowiadających przejściu w kierunku od korzenia do liścia. korzeń węzeł dr Pojęcia: • rodzic liść • przodek • potomek • rodzeństwo (dwa węzły są rodzeństwem, gdy mają tego samego ojca) og a Drzewa binarne Drzewo binarne jest skończonym zbiorem węzłów, który jest albo pusty, albo zawiera korzeń oraz dwa drzewa binarne. T AA B A C B D E F C D E F • Każdy węzeł przechowuje dwa wskaźniki: do lewego poddrzewa LLINK i prawego poddrzewa RLINK • T jest wskaźnikiem do drzewa • Jeśli T = Λ - drzewo puste Wpp T jest adresem korzenia drzewa, a LLINK(T) wskazuje lewe poddrzewo, RLINK(T) wskazuje prawe poddrzewo. Przechodzenie drzewa Jest to systematyczne przeglądanie węzłów w taki sposób, ze każdy węzeł jest odwiedzony dokładnie jeden raz. Przejście drzewa wyznacza porządek liniowy w drzewie. Sposoby przechodzenia drzewa binarnego: • preorder (porządek przedrostkowy) • inorder (porządek wrostkowy) • postorder (porządek przyrostkowy) Rekurencyjna definicja porządków przechodzenia drzewa binarnego: Jeśli drzewo jest puste, stop Wpp wykonaj: Przechodzenie preorder Przechodzenie inorder Odwiedź korzeń Przejdź lewe poddrzewo Przejdź lewe poddrzewo Odwiedź korzeń Przejdź prawe poddrzewo Przejdź prawe poddrzewo Przechodzenie postorder Przejdź lewe poddrzewo Przejdź prawe poddrzewo Odwiedź korzeń Przykład: AA B D C E F G Porządek preorder: ABDCEGFHI Porządek inorder: DBAEGCHFI Porządek postorder: DBGEHIFCA H I Algorytm: przechodzenie drzewa binarnego w porządku inorder Niech T będzie wskazuje na drzewo binarne. Algorytm korzysta z pomocniczego stosu S. K1. Inicjalizowanie. Utwórz stos pusty S; P ← T (P jest pomocniczą zmienną wskaźnikową). K2. Sprawdzenie, czy P = Λ? Jeśli P = Λ, to K4. K3. S ← P (włóż P na stos S); AA Niech P ← LLINK(P); Idź do K2. B C K4. Jeśli stos S = Λ, koniec algorytmu; Wpp P ← S; D E F K5. Odwiedzenie P. Odwiedź NODE(P); G H I P ← RLINK(P); Idź do K2. Przykłady wykorzystania drzew: • drzewiasta struktura algorytmów • diagramy organizacyjne przedsiębiorstw, drzewa gry Sortowanie drzewiaste Rozważamy drzewo binarne – każdy węzeł ma co najwyżej dwóch potomków, zwanych lewostronnym i prawostronnym. Algorytm sortowania drzewiastego: 1. przekształć listę wejściową w binarne drzewo poszukiwań T 2. obejdź drzewo T w porządku inorder i wypisz każdy element przy okazji drugich odwiedzin. Binarne drzewo poszukiwań T: każdy element binarnego drzewa poszukiwań ma tę własność, że jego lewostronne potomstwo jest mniejsze co do wartości od tego elementu, a prawostronne potomstwo jest większe. procedura obejdz_drzewo(T){ if(T==0) koniec; //jeśli drzewo T jest puste else{ obejdz_drzewo(lewe(T)); wypisz(korzen(T)); //wpisz element danych znajdujący się w korzeniu obejdz_drzewo(prawe(T)); } } Przykład sortowania drzewiastego (1) Budowa binarnego drzewa poszukiwań T 128 76 106 402 100 46 354 1018 112 28 396 128 76 46 28 106 100 35 402 354 112 1018 396 35 Przykład sortowania drzewiastego (2) Procedura obejdz_drzewo(T) 128 76 46 402 106 28 354 100 112 1018 396 35 128 46 76 28 28 35 35 35 28 46 46 76 106 100 100 100 106 112 112 112 106 76 128 402 354 354 396 396 396 354 402 1018 1018 1018 402 128 Bazy danych i bazy wiedzy Przykłady baz danych: • dane finansowe i osobowe przedsiębiorstwa • dane katalogowe biblioteki • rezerwacja biletów lotniczych Podstawowe modele baz danych • relacyjny – organizacja danych w postaci tabel • hierarchiczny – organizacja danych w postaci drzew lub sieci Rozwinięciem baz danych są bazy wiedzy Języki programowania Kompilacja – proces, w którym program w języku wysokiego poziomu jest tłumaczony na język adresów symbolicznych (asembler). Program realizujący ten proces nazywany jest kompilatorem. Interpretator – program tłumaczący każdą instrukcję na instrukcje poziomu maszyny i natychmiast ją wykonujący. Fortran Cobol Pascal, C++ Snobol Prolog Lisp Delphi Java do obliczeń numerycznych. język dla przedsiębiorstw i handlu (mechanizmy definiowania struktury pliku) języki uniwersalne manipulowanie tekstami i napisami oparty na logice faktów przetwarzanie list, obliczenia symboliczne Podstawowe metody rozwiązywania zadań algorytmicznych • metoda dziel i zwyciężaj • algorytmy zachłanne • planowanie dynamiczne Metoda dziel i zwyciężaj Problem dzieli się na mniejsze zadania tego samego typu i rozwiązuję się zdefiniowane podzadania. Następnie łączy się częściowe rozwiązania w rozwiązanie całościowe problemu wyjściowego. Jeżeli zdefiniowane podzadania są dokładnie takie same jak zadanie wyjściowe, lecz postawione dla „mniejszych” lub „prostszych” danych, to algorytm rozwiązania może być rekurencyjny. Przykład zastosowania metody dziel i zwyciężaj: wieże Hanoi – algorytm rozwiązuje zadanie dla N krążków, dzieląc problem na dwa problemy dla N-1 krążków i rozwiązując je. Metoda dziel i zwyciężaj Poszukiwanie najmniejszego i największego elementu w nieuporządkowanej liście L Rozwiązanie nr I Przejdź listę L dokładnie jeden raz, przechowując bieżące maksimum i minimum, i cały czas porównując te wartości z kolejnymi elementami. Rozwiązanie nr II – zastosowanie metody dziel i zwyciężaj struktura minmax{ element_listy min, max; }; minmax Znajdź_min_max(lista L){ minmax wynik,wynik1,wynik2; if(dlugosc_listy(L)==1){ wynik.min ← wynik.max ← L[1]; return wynik; } podziel_liste(L,L1,L2); //podziel listę L na połowy L1 o L2 wynik1 ← Znajdź_min_max(L1); wynik2 ← Znajdź_min_max(L2); wynik.min ← minimum(wynik1.min, wynik2.min); wynik.max ← maximum(wynik1.max, wynik2.max); return wynik; } Metoda dziel i zwyciężaj Sortowanie przez scalanie 128 46 76 28 35 5 18 2 podział 128 46 76 35 28 5 podział 128 128 28 35 76 5 ... scalanie 46 28 28 28 76 128 128 76 5 2 scalanie 46 rekurencyjne wywołanie sortowania przez scalanie 2 18 35 2 35 76 46 5 2 18 28 5 35 5 18 76 128 18 46 18 scalanie 18 5 2 35 28 2 ... 76 ... 46 128 2 podział ... 46 18 35 Algorytmy zachłanne Zastosowanie – znajdowanie dla danego problemu rozwiązania pod pewnymi względami „najlepszego” z wszystkich możliwych rozwiązań. Przykład Dany jest zbiór miast. Zadanie polega na połączeniu miast w sieć w ten sposób, żeby z dowolnego miasta można było dotrzeć do każdego innego w „najtańszy” (najkrótszy) sposób. Założenia: 1. koszt bezpośredniego połączenia z jednego miasta do drugiego jest wprost proporcjonalny do odległości między nimi 2. znane są tylko odległości dla par miast, dla których jest możliwe bezpośrednie połączenie. Algorytmy zachłanne Sieć połączeń między miastami nazywana jest grafem (grafem etykietowanym). Rozwiązanie tego zadania sprowadza się do znalezienia minimalnego drzewa rozpinającego. Minimalne drzewo rozpinające grafu to drzewo, które 1. dociera do każdego węzła grafu dokładnie raz i 2. suma etykiet krawędzi grafu jest najmniejsza z możliwych. etykieta krawędź 103 2 2 20 37 21 15 węzeł 5 10 20 9 5 8 18 26 5 graf miast 15 8 10 9 9 9 5 minimalne drzewo rozpinające dla grafu miast Algorytmy zachłanne Budowa minimalnego drzewa rozpinającego metodą zachłanną: 1. Skonstruuj drzewo zdegenerowane, składające się z najtańszej krawędzi grafu. 2. W każdym kolejnym kroku dodaj do już istniejącego drzewa najtańszą krawędź z krawędzi dotąd nie wziętych pod uwagę (uwaga! dodanie nowej krawędzi nie może prowadzić do powstania cyklu, w takim przypadku przejdź do nowej krawędzi w porządku rosnących kosztów). 2 2 2 5 2 5 2 5 5 10 5 2 5 2 9 5 9 2 15 5 8 10 5 9 5 8 10 5 5 9 8 10 9 5 9 9 Programowanie (planowanie) dynamiczne Zastosowanie – poszukiwanie minimalnej ścieżki, czyli najtańszej drogi od początkowego węzła do węzła przeznaczenia w grafie. Przykład Dany jest spójny graf miast, który jest skierowany i acykliczny. Znajdź najkrótszą ścieżkę z miasta A do miasta B. Graf jest spójny, jeśli istnieje ścieżka pomiędzy dwoma dowolnymi węzłami grafu. Graf skierowany – jeżeli istnieje bezpośrednie połączenie między dwoma węzłami, to jest to połączenie tylko w jedną stronę. Graf jest acykliczny, jeśli nie zawiera cykli. Programowanie (planowanie) dynamiczne Planowanie dynamiczne jest poszukiwaniem optymalnego ciągu wyborów. Ciąg optymalny jest najlepszym wyborem z wszystkich kombinacji powstałych z: 1. dokonania konkretnego wyboru 2. znalezienia optymalnego wyboru części ciągu pozostałych wyborów. 2 C 3 5 A 11 F A 7 5 D B 7 6 C F E 3 14 C 6 G Graf miast E 3 A D B 6 3 5 F E 5 D B 6 G Metoda zachłanna G Planowanie dynamiczne W algorytmie planowania dynamicznego długość najkrótszej ścieżki wiodącej z A do B powstaje przez znalezienie: Krok 1. Najmniejszej z trzech odległości z A do C, D, G, Krok 2. Dodanie do tych odległości długości najkrótszej ścieżki prowadzącej z C, D i G do B Krok 3. Wybór najkrótszej z nich. Programowanie (planowanie) dynamiczne Niech L(X) oznacza najkrótszą ścieżkę prowadzącą z X do B. Wtedy: L(A) = min(5 + L(C), 14 + L(G), 3 + L(D)). Najpierw znajdowane są trzy „mniejsze” ścieżki optymalne: L(C), L(G), L(D), a później wykonując dodawania i porównania otrzymywana jest całkowita ścieżka optymalna: L(C) = min(2 + L(F), 3 + L(E)) L(G) = min(7 + L(E), 6 + L(B)) L(D) = min(7 + L(E), 6 + L(G), 11 + L(C)) ........... L(B) = 0 2 C 3 5 A 11 F E 3 7 5 D 14 B 7 6 6 G Programowanie (planowanie) dynamiczne Algorytm znajdowania najkrótszej ścieżki w grafie Jeśli węzłami są C1, .., CN i ścieżka ma się rozpocząć w C1 i skończyć w CN, to algorytm wymaga obliczenia optymalnej ścieżki częściowej L(CI), przedstawiającej najkrótszą ścieżkę z CI do CN, dla każdego I=1..N: L(CI) == min( odległość(CI,CK) + L(CK) ) przy czym CK to wszystkie węzły, do których prowadzą bezpośrednio krawędzie z CI. Z założenia graf jest acykliczny, możliwe jest więc obliczenie wszystkich L(CI), posuwając się z B do tyłu: 1. Obliczamy L(F) = 7, L(E) = 5, L(G) = 6 2. Obliczamy L(C) = min(2 + L(F), 3 + L(E)) = 3 + L(E) = 8 3. Obliczamy L(D) = min(11 + L(C), 6 + L(G)) = 6 + L(G) = 12 4. Obliczamy L(A) = min(5 +L(C), 3 + L(D), 14 + L(G)) = 5 + L(C) = 13 Optymalna ścieżka z A do B to: A → C → E → B 2 C 3 5 A 11 F E 3 7 5 D 14 B 7 6 6 G