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