• Nie Znaleziono Wyników

Wstęp do programowania Zastosowania stosów i kolejek

N/A
N/A
Protected

Academic year: 2021

Share "Wstęp do programowania Zastosowania stosów i kolejek"

Copied!
48
0
0

Pełen tekst

(1)

© Piotr Chrząstowski-Wachtel

Wstęp do programowania

Zastosowania stosów i kolejek

(2)

2

FIFO - LIFO

Kolejki i stosy służą do przechowywania wartości zbiorów dynamicznych, czyli takich, które

powstają przez dodawanie i usuwanie elementów zaczynając od zbioru pustego.

Organizacja tych struktur danych odpowiada dwóm podstawowym metodom pobierania

elementów ze zbioru w zależności od kolejności ich wkładania.

FIFO: First In First Out LIFO: Last In First Out

(3)

3

Przechodzenie grafu

Grafy najczęściej reprezentujemy za pomocą list

sąsiedztwa. Wygodnie jest ponumerowac węzły grafu od 1 do n i w tablicy S[1..n]of węzeł

przechowywać informację o sąsiadach węzłów.

type węzeł = record

val:typ_elementu;

odwiedzony:Boolean;

sąsiedzi:lista;

end;

Zakładamy też, że mamy dostęp do struktury danych reprezentującej zbiór. Nazwijmy go magazynem.

(4)

4

Przechodzenie grafu

Ogólny schemat przejścia grafu jest więc taki.

Zaczynamy od inicjalizacji struktury danych:

S[1].odwiedzony:=true;

for i:=2 to n do

S[i].odwiedzony:=false;

magazyn:= [1];

Następnie w pętli przetwarzamy graf następująco:

(5)

5

Przechodzenie grafu

While magazyn <> [] do begin

pobierz(x,magazyn); {jakieś x}

przetwórz(x);

l:=S[x].sąsiedzi;

while l<> nil do begin

if not S[l^.w].odwiedzony then begin S[l^.w].odwiedzony:=true;

wstaw(x,magazyn) end;

l:=l^.nast end

end

(6)

6

DFS - BFS

Ten sposób przejścia grafu gwarantuje nam

odwiedzenie każdego węzła osiągalnego z węzła o numerze 1.

przetworzenie każdego węzła dokładnie raz

Jeśli magazyn zrealizujemy za pomocą stosu, to otrzymamy algorytm przechodzenia grafu wgłąb

(DFS: Depth-First-Search), a jesli za pomocą kolejki, to otrzymamy algorytm przechodzenia grafu wszerz (BFS: Breadth-First-Search).

Oczywiście można sobie wyobrazić i dowolną inną kolejność pobierania elementów z magazynu, ale te dwie są najważniejsze.

(7)

7

Przykład

Naszym zadaniem jest wypisanie wszystkich numerów węzłów grafu w porządku DFS

Graf wejściowy:

4 3

1

7

6 5

2

(8)

8

Przykład: DFS

Stos: 1 Wyjście:

4 3

1

7

6 5

2

(9)

9

DFS

Stos: 2 5 Wyjście: 1

4 3

1

7

6 5

2

(10)

10

DFS

Stos: 2 3 6 Wyjście: 1 5

4 3

1

7

6 5

2

(11)

11

DFS

Stos: 2 3 7

Wyjście: 1 5 6

4 3

1

7

6 5

2

(12)

12

DFS

Stos: 2 3 4

Wyjście: 1 5 6 7

4 3

1

7

6 5

2

(13)

13

DFS

Stos: 2 3

Wyjście: 1 5 6 7 4

4 3

1

7

6 5

2

(14)

14

DFS

Stos: 2

Wyjście: 1 5 6 7 4 3

4 3

1

7

6 5

2

(15)

15

DFS

Stos:

Wyjście: 1 5 6 7 4 3 2

4 3

1

7

6 5

2

(16)

16

Przykład: BFS

Kolejka: 1 Wyjście:

4 3

1

7

6 5

2

(17)

17

Przykład: BFS

Kolejka: 2 5 Wyjście: 1

4 3

1

7

6 5

2

(18)

18

Przykład: BFS

Kolejka: 5 3 Wyjście: 1 2

4 3

1

7

6 5

2

(19)

19

Przykład: BFS

Kolejka: 3 6 Wyjście: 1 2 5

4 3

1

7

6 5

2

(20)

20

Przykład: BFS

Kolejka: 6 4 7 Wyjście: 1 2 5 3

4 3

1

7

6 5

2

(21)

21

Przykład: BFS

Kolejka: 4 7

Wyjście: 1 2 5 3 6

4 3

1

7

6 5

2

(22)

22

Przykład: BFS

Kolejka: 7

Wyjście: 1 2 5 3 6 4

4 3

1

7

6 5

2

(23)

23

Przykład: BFS

Kolejka:

Wyjście: 1 2 5 3 6 4 7

4 3

1

7

6 5

2

(24)

24

Cecha przeszukiwania wszerz

Przy przeszukiwaniu wszerz węzły są odwiedzane w kolejności zgodnej z odległością od źródła (liczoną liczbą krawędzi).

Dowód (indukcja) tezy, że w momencie wstawienia do kolejki węzła v wszystkie węzły bliższe niż v

znajdują się w kolejce lub zostały już przetworzone:

Pierwszym węzłem przetworzonym jest źródło.

Aby węzeł v o numerze niebędący źródłem mógł być

przetworzony, musi być wstawiony do kolejki przez inny węzeł v', o odległości mniejszej o 1, który właśnie został pobrany z kolejki. Na mocy założenia indukcyjnego nie było w niej węzłów bliższych niż v' – zostały już

przetworzone.

(25)

25

Antyczne zadanie

Sprzedawca wina ma trzy amfory o pojemności 3,5 i 8 litrów. Największa z nich jest wypełniona winem, pozostałe dwie są puste. Chciałby odlać z największej amfory do średniej 4 litry. Jak to można zrobić, przy założeniu że nie ma litrowej miarki, a to co pozostaje to wykonywanie przelewań dwóch typów:

Nalanie wina z jednej amfory do drugiej do pełna

Przelanie całej zawartości jednej amfory do drugiej

(26)

26

Rozwiązanie:

Konstruujemy graf konfiguracji (zawartości wina w 3 amforach). Źródłem jest konfiguracja (8,0,0).

Konfiguracje dozwolone, to takie trójki (x,y,z). że

x+y+z=8

xyz(x-8)(y-5)(z-3)=0

Konfiguracja (x,y,z) jest połączona krawędzią z (x',y',z') jeśli z (x,y,z) można otrzymać (x',y',z') za pomocą jednego przelania.

Chodzi o znalezienie najkrótszej ścieżki w tym grafie łączącej źródło z węzłem (4,4,0).

(27)

27

Zadanie o katastrofach lotniczych

Mamy chronologiczne dane z katastrof lotniczych, w których kolejno są zapisane daty i liczby ofiar. Naszym zadaniem jest określić dla każdej katastrofy, od ilu lat nie było większej.

Przykład:

47 48 50 50 51 52 53 55 56 57 57 58 60 8 3 6 4 7 2 4 1 3 9 4 5 2

(28)

28

Zadanie o katastrofach lotniczych

Mamy chronologiczne dane z katastrof lotniczych. Dane zawierają kolejno daty i liczby ofiar. Naszym zadaniem jest określić dla każdej katastrofy, od ilu lat nie było

większej.

Przykład:

47 48 50 50 51 52 53 55 56 57 57 58 60 8 3 6 4 7 2 4 1 3 9 4 5 2 -1 1 3 0 4 1 2 2 3 -1 0 1 2

(29)

29

Parę obserwacji

Po pierwsze zauważmy, że przeglądając dane kolejno od nastarszej musimy odwoływać się do wcześniej napotkanych, ale z zastrzeżeniem, że jeśli jakaś

katastrofa X jest wcześniejsza i ma mniejszą liczbę ofiar niż Y, to do X już nigdy później niż Y się nie

odwołamy.

Po drugie z danego roku warto pamiętać tylko

największą katastrofę, nawet jeśli po niej tego samego roku zdarzyła się mniejsza.

Z tych dwóch uwag wynika, że będą nam potrzebne informacje o katastrofach o malejących liczbach ofiar i rosnących latach. Umieścimy je na stosie.

(30)

30

Typy danych

type katastrofa = record lofiar,

rok,

odkiedy : Integer;

end;

typ = katastrofa; {typ elementu listy}

var l:lista; {katastrof}

s:stos; {katastrof}

d:katastrofa; {rekord roboczy}

Zkładamy, że dane w liście l mają wypełnione pola lofiar i rok, a mamy uzupełnić pole odkiedy.

(31)

31

Rozwiązanie zadania o katastrofach lotniczych

MakeEmpty(s);

d.lofiar:= ∞; Push(s,d); {strażnik}

while l<>nil do begin repeat Pop(s,d)

until d.lofiar > l^.w.lofiar;

if d.lofiar = ∞ then l^.w.odkiedy:=-1 else l^.w.odkiedy:=d^.w.rok – l^.w.rok;

Push(s,d);

if d^.w.rok<l^.w.rok then Push(s,l^.w);

l:=l^.nast end

(32)

32

Wizualizacja rozwiązania

47 48 50 50 51 52 53 55 56 57 57 58 60 8 3 6 4 7 2 4 1 3 9 4 5 2

(33)

33

Wizualizacja rozwiązania

47 48 50 50 51 52 53 55 56 57 57 58 60 8 3 6 4 7 2 4 1 3 9 4 5 2 -1

(8,47) (∞,?)

(34)

34

Wizualizacja rozwiązania

47 48 50 50 51 52 53 55 56 57 57 58 60 8 3 6 4 7 2 4 1 3 9 4 5 2 -1 1

(3,48) (8,47) (∞,?)

(35)

35

Wizualizacja rozwiązania

47 48 50 50 51 52 53 55 56 57 57 58 60 8 3 6 4 7 2 4 1 3 9 4 5 2 -1 1 3

(6,50) (8,47) (∞,?)

(36)

36

Wizualizacja rozwiązania

47 48 50 50 51 52 53 55 56 57 57 58 60 8 3 6 4 7 2 4 1 3 9 4 5 2 -1 1 3 0

(6,50) (8,47) (∞,?)

(37)

37

Wizualizacja rozwiązania

47 48 50 50 51 52 53 55 56 57 57 58 60 8 3 6 4 7 2 4 1 3 9 4 5 2 -1 1 3 0 4

(7,51)

(8,47) (∞,?)

(38)

38

Wizualizacja rozwiązania

47 48 50 50 51 52 53 55 56 57 57 58 60 8 3 6 4 7 2 4 1 3 9 4 5 2 -1 1 3 0 4 1

(2,52) (7,51) (8,47) (∞,?)

(39)

39

Wizualizacja rozwiązania

47 48 50 50 51 52 53 55 56 57 57 58 60 8 3 6 4 7 2 4 1 3 9 4 5 2 -1 1 3 0 4 1 2

(4,54) (7,51) (8,47) (∞,?)

(40)

40

Wizualizacja rozwiązania

47 48 50 50 51 52 53 55 56 57 57 58 60 8 3 6 4 7 2 4 1 3 9 4 5 2 -1 1 3 0 4 1 2 2

(1,55) (4,54) (7,51) (8,47) (∞,?)

(41)

41

Wizualizacja rozwiązania

47 48 50 50 51 52 53 55 56 57 57 58 60 8 3 6 4 7 2 4 1 3 9 4 5 2 -1 1 3 0 4 1 2 2 3

(3,56) (4,54) (7,51) (8,47) (∞,?)

(42)

42

Wizualizacja rozwiązania

47 48 50 50 51 52 53 55 56 57 57 58 60 8 3 6 4 7 2 4 1 3 9 4 5 2 -1 1 3 0 4 1 2 2 3 -1

(9,57) (∞,?)

(43)

43

Wizualizacja rozwiązania

47 48 50 50 51 52 53 55 56 57 57 58 60 8 3 6 4 7 2 4 1 3 9 4 5 2 -1 1 3 0 4 1 2 2 3 -1 0

(9,57) (∞,?)

(44)

44

Wizualizacja rozwiązania

47 48 50 50 51 52 53 55 56 57 57 58 60 8 3 6 4 7 2 4 1 3 9 4 5 2 -1 1 3 0 4 1 2 2 3 -1 0 1

(5,58) (9,57) (∞,?)

(45)

45

Wizualizacja rozwiązania

47 48 50 50 51 52 53 55 56 57 57 58 60 8 3 6 4 7 2 4 1 3 9 4 5 2 -1 1 3 0 4 1 2 2 3 -1 0 1 2

(2,60) (5,58) (9,57) (∞,?)

(46)

46

Analiza złożoności

Na pierwszy rzut oka wygląda na to, że nasz algorytm ma złożoność kwadratową. Mamy bowiem w pętli while o liczbie obrotów O(n) wewnętrzną pętlę repeat o liczbie obrotów pwsymistycznie O(n). Wiemy przecież, że

O(n)*O(n) jest O(n2). Pokażemy, że wbrew pierwszej intuicji złożoność całego algorytmu jest O(n).

Dowód jest banalny. Zauważmy, że operacją dominującą jest instruksja Pop(s,d) wewnątrz pętli repeat. Ale ile razy można zdjąć coś ze stosu? Na pewno nie więcej, niż się włożyło, a łącznie operacji Push wykonaliśmy co

najwyyzej 2n.

(47)

47

Analiza złożoności pamięciowej

Z pamięcią jest nawet nieco lepiej. Ponieważ lata

elementów na stosie maleją w kierunku dna, więc nigdy rozmiar stosu nie przekroczy zakresu dat.

(48)

48

Zadanie o optymalnym dodawaniu

Z wykładu o arytmetyce liczb rzeczywistych pamiętamy zadanie o optymalnym (najdokładniejszym) dodawaniu n liczb. Załóżmy, że oryginalne dane są posortowane.

Algorytm optymalnego dodawania może stać się liniowy, jesli użyjemy stosu i kolejki! Wkładamy na stos malejąco do góry wszystkie elementy. Naszym zadaniem jest

pobranie dwóch najmniejszych elementów, dodanie ich i włożenie z powrotem sumy do zbioru argumentów.

Uzyskane sumy wkładamy do kolejki. Bedą one rosnące.

Zawsze dwa najmniejsze elementy będą się znajdowały pośród czterech: 2 na szczycie stosu i 2 na początku

kolejki! (lub mniej)

Cytaty

Powiązane dokumenty

However, it should be noted that, if the given problem instance is unsolvable, then the steps taken by such useless search instances are the only overhead (in terms of the number

➤ If the branching factor for all nodes is finite, breadth-first search is guaranteed to find a solution if one exists.. It is guaranteed to find the path with

• f 1, podającej koszt przejścia od stanu początkowego do stanu bieżącego; war- tość ta jest dokładna, gdyż dotyczy ona stanów, w których już byliśmy i do- kładnie

Graf jest spójny, jeżeli z każdego wierzchołka da się dojść do wszystkich pozostałych. W przeciwnym wypadku graf jest określany jako

Because of its ultralow background level [21] and excellent energy resolution [ ∼3.6 and ∼3.0 keV full width at half maximum (FWHM) for coaxial and BEGe detectors at Q ββ ¼ 2039

1 Jeżeli wszystkie wartości wykorzystywane w obliczeniach są tego samego typu, wynik też jest tego typu (z wyjątkiem operacji porównania). 2 Wartości różnych typów są

Strategia z nawracaniem jest modyfikacją algorytmu przeszukiwania „w głąb”. Lista OPEN

– uzyskanie co najmniej 50 punktów łącznie. – uzyskanie co najmniej 20 punktów