algorytmy i podstawy programowania - gagolewski.com · algorytmy i podstawy programowania 5....

15
MAREK G , AGOLEWSKI INSTYTUT BADA ´ N SYSTEMOWYCH PAN WYDZIA L MATEMATYKI I NAUK INFORMACYJNYCH POLITECHNIKI WARSZAWSKIEJ Algorytmy i podstawy programowania Materialy dydaktyczne dla student´ ow matematyki na Wydziale Matematyki i Nauk Informacyjnych Politechniki Warszawskiej Ostatnia aktualizacja: 1 pa´ zdziernika 2016 r. Copyright © 2010–2016 Marek G , agolewski This work is licensed under a Creative Commons Attribution 3.0 Unported License.

Upload: hacong

Post on 27-Feb-2019

237 views

Category:

Documents


0 download

TRANSCRIPT

MAREK G ↪AGOLEWSKIINSTYTUT BADAN SYSTEMOWYCH PAN

WYDZIA L MATEMATYKI I NAUK INFORMACYJNYCH POLITECHNIKI WARSZAWSKIEJ

Algorytmyi podstawy programowania

5. Rekurencja. Abstrakcyjne typy danych

Materia ly dydaktyczne dla studentow matematykina Wydziale Matematyki i Nauk Informacyjnych Politechniki Warszawskiej

Ostatnia aktualizacja: 1 pazdziernika 2016 r.

Copyright © 2010–2016 Marek G ↪agolewskiThis work is licensed under a Creative Commons Attribution 3.0 Unported License.

SPIS TRESCI 0

Spis tresci

5.1. Rekurencja . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15.1.1. Przyk lad: silnia . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25.1.2. Przyk lad: NWD . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25.1.3. Przyk lad: wieze z Hanoi . . . . . . . . . . . . . . . . . . . . . . . . . 35.1.4. Przyk lad (niem ↪adry): liczby Fibonacciego . . . . . . . . . . . . . . . 5

5.2. Podstawowe abstrakcyjne typy danych . . . . . . . . . . . . . . . . . . . . . 75.2.1. Stos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85.2.2. Kolejka . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95.2.3. Kolejka priorytetowa . . . . . . . . . . . . . . . . . . . . . . . . . . . 105.2.4. S lownik . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10

5.3. Cwiczenia . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13

Ostatnia aktualizacja: 1 pazdziernika 2016 r.

5.1. REKURENCJA 1

5.1. Rekurencja

Z rekurencj ↪a (b ↪adz rekursj ↪a, ang. recursion) mamy do czynienia wtedy, gdy w definicjipewnego obiektu pojawia si ↪e odniesienie do niego samego.

Rozwazmy najpierw rys. 5.1. Przedstawia on pewn ↪a znan ↪a, zacn ↪a dam ↪e, trzymaj ↪ac ↪aobraz, ktory przedstawia j ↪a sam ↪a trzymaj ↪ac ↪a obraz, na ktorym znajduje si ↪e ona sama trzy-maj ↪aca obraz. . . Podobny efekt moglibysmy uzyskac filmuj ↪ac kamer ↪a telewizor pokazuj ↪acyto, co w lasnie nagrywa kamera.

Rys. 5.1. Rekurencyjna Mona Lisa

A teraz popatrzmy na rys. 5.2 przedstawiaj ↪acy prosty fraktal. Przypatruj ↪ac si ↪e d luzejtej strukturze widzimy, ze ma ona bardzo prost ↪a konstrukcj ↪e. Wydaje si ↪e, ze narysowaniekszta ltu takiego drzewa odbywa si ↪e nast ↪epuj ↪aco. Rysujemy odcinek o pewnej d lugosci. Na-st ↪epnie obrociwszy si ↪e nieco w lewo/prawo, rysujemy nieco krotszy odcinek. W punktach,w ktorych zakonczylismy rysowanie, czynimy podobnie itd.

Rys. 5.2. Fraktalne drzewo

W przypadku j ↪ezykow programowania mowimy o rekurencji, gdy funkcja wywo lujesam ↪a siebie. Czyni ↪ac tak jednak we

”frywolny”sposob spowodowalibysmy zawieszenie kom-

Ostatnia aktualizacja: 1 pazdziernika 2016 r.

5.1. REKURENCJA 2

putera. Istotn ↪a cech ↪a rekurencji jest wi ↪ec dodatkowo warunek stopu, czyli zdefiniowanieprzypadku, w ktorym rekurencyjne wywo lanie zostaje przerwane.

Zapami↪etaj

Kazde wywo lanie funkcji (takze rekurencyjne) powoduje utworzenie nowego zestawuzmiennych lokalnych!

Rekurencja, jak widzimy, jest bardzo prost ↪a technik ↪a, ktorej opanowanie pozwoli namtworzyc bardzo ciekawe i cz ↪esto latwe do zaprogramowania algorytmy. Wiele bowiem obiek-tow (np. matematycznych) jest w lasnie ze swej natury zdefiniowana rekurencyjnie.

W ponizszych paragrafach rozwazymy kilka ciekawych przyk ladow takich zagadnien.

5.1.1. Przyk lad: silnia

W ponizszej definicji silni pojawia si ↪e odniesienie do. . . silni. Zauwazmy jednak, ze obiektten jest dobrze okreslony, gdyz podany zosta l warunek stopu.{

0! = 1,n! = n (n− 1)! dla n > 0.

Funkcj ↪e s luz ↪ac ↪a do obliczenia silni mozna napisac opieraj ↪ac si ↪e wprost na powyzszymwzorze.

1 int silnia (int n)2 {3 assert (n >= 0); // u p e w n i j m y s i ↪e . . .4 if (n == 0) return 1;5 else return n∗ silnia (n−1);6 }

Dla porownania przyjrzyjmy si ↪e, jakby mog la wygl ↪adac analogiczna funkcja niekorzysta-j ↪aca z dobrodziejstw rekurencji.

1 int silnia2 (int n)2 {3 assert (n >= 0);4 int w = 1;5 for (int i=1; i<=n; ++i)6 w ∗= i;7 return w;8 }

To, ktor ↪a wersj ↪e uwazamy za czytelniejsz ↪a, zalezy oczywiscie od nas samych.

5.1.2. Przyk lad: NWD

Poznalismy juz algorytm wyznaczania najwi ↪ekszego wspolnego dzielnika dwoch liczb natu-ralnych. Okazuje si ↪e, ze NWD mozna takze okreslic ponizszym rownaniem rekurencyjnym.Niech 1 ¬ n ¬ m. Wowczas:

NWD(n, m) ={

m dla n = 0,NWD(m mod n, n) dla n > 0.

Definicja ta przek lada si ↪e bezposrednio na nast ↪epuj ↪acy kod w j ↪ezyku C++.

Ostatnia aktualizacja: 1 pazdziernika 2016 r.

5.1. REKURENCJA 3

1 int nwd(int n, int m)2 {3 assert (0 <= n && n <= m);4 if (n == 0) return m;5 else return nwd(m % n, n);6 }

I w tym przypadku latwo jest napisac rownowazn ↪a wersj ↪e nierekurencyjn ↪a:

1 int nwd(int n, int m)2 {3 assert (0 <= n && n <= m);4 while (n != 0) {5 int c = n;6 n = m % n;7 m = c;8 }9 return m;

10 }

5.1.3. Przyk lad: wieze z Hanoi

A teraz zabawna lamig lowka. Za lozmy, ze danych jest n kr ↪azkow o roznych srednicachoraz trzy s lupki. Pocz ↪atkowo wszystkie kr ↪azki znajduj ↪a si ↪e na s lupku nr 1, w kolejnosciod najwi ↪ekszego (do l) do najmniejszego (gora). Sytuacj ↪e wyjsciow ↪a dla n = 4 przedstawiarys. 5.3.

4

3

2

1

Rys. 5.3. Sytuacja wyjsciowa dla 4 kr ↪azkow w problemie wiez z Hanoi.

Cel: przeniesienie wszystkich kr ↪azkow na s lupek nr 3.

Zasada #1: kr ↪azki przenosimy pojedynczo.

Zasada #2: kr ↪azek o wi ↪ekszej srednicy nie moze znalezc si ↪e na kr ↪azku o mniejszejsrednicy.

Zastanowmy si ↪e, jak by mog l wygl ↪adac algorytm s luz ↪acy do rozwi ↪azywania tego pro-blemu. Niech b ↪edzie to funkcja Hanoi(k, A, B, C), ktora przek lada k kr ↪azkow ze s lupka Ana s lupek C z wykorzystaniem s lupka pomocniczego B, gdzie A,B,C∈ {1, 2, 3}. Wykonywanyruch b ↪edzie wypisywany na ekranie.

Aby przeniesc n klockow ze s lupka A na C, nalezy przestawic n−1 mniejszych elementowna s lupek pomocniczy B, przeniesc n-ty kr ↪azek na C i potem pozosta le n − 1 kr ↪azkowprzestawic z B na C. Jest to, rzecz jasna, sformu lowanie zawieraj ↪ace elementy rekurencji.

Ostatnia aktualizacja: 1 pazdziernika 2016 r.

5.1. REKURENCJA 4

Wywo lanie pocz ↪atkowe: Hanoi(n, 1, 2, 3).

Rozwi ↪azanie w j ↪ezyku C++:

1 void Hanoi(int k, int A, int B, int C)2 {3 if (k >0) // warunek s t o p u4 {5 Hanoi(k−1, A, C, B);6 cout << A << "−>" << C << endl;7 Hanoi(k−1, B, A, C);8 }9 }

Zauwazmy, ze stworzenie wersji nierekurencyjnej powyzszej funkcji by loby dla nas bar-dzo trudne – w przeciwienstwie do przyk ladow z silni ↪a i NWD, gdzie rekurencja latwosi ↪e ”

rozpl ↪atywa la”. Podczas bardziej zaawansowanego kursu algorytmiki dowiemy si ↪e, zekazd ↪a funkcj ↪e stosuj ↪ac ↪a rekurencj ↪e mozemy zapisac w postaci iteracyjnej przy uzyciu po-mocniczej struktury danych typu stos. Cz ↪esto jest to jednak zadanie nietrywialne i wynikpozostawia wiele do zyczenia pod wzgl ↪edem czytelnosci.

Ciekawostka

Interesuj ↪acym zagadnieniem jest wyznaczenie liczby przestawien potrzebnych do rozwi ↪azania lamig lowki. W zaproponowanym algorytmie jest to:{

L(1) = 1,L(n) = L(n− 1) + 1 + L(n− 1) dla n > 0.

Mozna pokazac, ze jest to optymalna liczba przestawien.Rozwi ↪azmy powyzsze rownanie rekurencyjne, aby uzyskac postac jawn ↪a rozwi ↪azania:

L(n) = 2L(n− 1) + 1,

L(n) + 1 = 2(L(n− 1) + 1).

Zauwazmy, ze L(n) + 1 tworzy ci ↪ag geometryczny o ilorazie 2. Zatem

L(1) + 1 = 2,

L(2) + 1 = 4,

L(3) + 1 = 8,

...

L(n) + 1 = 2n.

Wi ↪ecL(n) = 2n − 1.

Zagadka Wiez z Hanoi sta la si ↪e znana w XIX wieku dzi ↪eki matematykowi E. Lucasowi.Jak g losi tybetanska legenda, mnisi w swi ↪atyni Brahmy rozwi ↪azuj ↪a t ↪e lamig lowk ↪e prze-suwaj ↪ac 64 z lote kr ↪azki. Podobno, gdy skoncz ↪a oni swe zmagania, nast ↪api koniec swiata.Zak ladaj ↪ac jednak, ze nawet gdyby wykonanie jednego ruchu zajmowa lo tylko 1 sekund ↪e,to na pe lne rozwi ↪azanie i tak potrzeba 264 − 1 = 18446744073709551615 sekund, czylioko lo 584542 miliardow lat. To ponad 400 razy d luzej niz szacowany wiek Wszechswiata!

Ostatnia aktualizacja: 1 pazdziernika 2016 r.

5.1. REKURENCJA 5

5.1.4. Przyk lad (niem↪

adry): liczby Fibonacciego

Jako ostatni problem rozpatrzmy rownanie, do ktorego doszed l Leonard z Pizy (1202),rozwazaj ↪ac rozmnazanie si ↪e krolikow.

Sformu lowa l on nast ↪epuj ↪acy uproszczony model szacowania liczby par w populacji.Na pocz ↪atku mamy dan ↪a jedn ↪a par ↪e krolikow. Kazda para osi ↪aga p lodnosc po up lyni ↪eciujednej jednostki czasu od narodzenia. Z kazdej p lodnej pary rodzi si ↪e w kolejnej chwilijedna para potomstwa.

Liczb ↪e par krolikow w populacji w chwili n mozna opisac za pomoc ↪a tzw. ci ↪agu Fibo-nacciego.

F0 = 1,F1 = 1,Fn = Fn−1 + Fn−2 dla n > 1.

W wyniku otrzymujemy zatem ci ↪ag (1, 1, 2, 3, 5, 8, 13, 21, 34, . . . ).Bezposrednie prze lozenie powyzszego rownania na kod w C++ moze wygl ↪adac jak

nizej.

1 int fibrek (int n)2 {3 if (n > 1) return fibrek (n−1)+ fibrek (n−2);4 else return 1;5 }

Powyzszy algorytm rekurencyjny jest jednak bardzo nieefektywny! Przypatrzmy si ↪e,jak przebiegaj ↪a obliczenia dla wywo lania fibrek(5):

fibrek(5);fibrek(4);

fibrek(3);fibrek(2);

fibrek(1); #fibrek(0); #

fibrek(1); #fibrek(2);

fibrek(1); #fibrek(0); #

fibrek(3);fibrek(2);

fibrek(1); #fibrek(0); #

fibrek(1); #Wi ↪ekszosc wartosci jest niepotrzebnie liczona wielokrotnie.

Rozwazmy dla przyk ladu, ile potrzebnych jest operacji arytmetycznych (dodawanie) doznalezienia wartosci Fn za pomoc ↪a powyzszej funkcji. Liczb ↪e t ↪e mozna opisac rownaniem:

L(0) = 0,L(1) = 0,L(n) = L(n− 1) + L(n− 2) + 1 dla n > 1.

Rozpatruj ↪ac kilka kolejnych wyrazow, zauwazamy, ze L(n) = Fn−1 − 1 dla n > 0.Okazuje si ↪e, ze dla duzych n, L(n) jest proporcjonalne do cn dla pewnej sta lej c.

Zatem liczba krokow potrzebnych do uzyskania n-tej liczby Fibonacciego algorytmemrekurencyjnym rosnie wyk ladniczo, tj. bardzo (naprawd ↪e bardzo) szybko! Dla przyk ladu

Ostatnia aktualizacja: 1 pazdziernika 2016 r.

5.1. REKURENCJA 6

czas potrzebny do policzenia F40 na komputerze autora tych refleksji to 1,4 s, dla F45 tojuz 15,1 s, a dla F50 to az 2 minuty i 47 s.

Tym razem okazuje si ↪e, ze implementacja stosuj ↪aca rekurencj ↪e wprost jest wysocenieefektywne. Przyk lad ten jest jednak bardzo pouczaj ↪acy dla matematykow – nie zawsze

”przepisanie” definicji interesuj ↪acego nas obiektu b ↪edzie prowadzi lo do dobrej w praktyce

metody obliczeniowej1

Zatem w tym przypadku powinnismy uzyc raczej”zwyk lego” rozwi ↪azania iteracyjnego,

ktore – co istotne – wykorzystuje tylko O(n) operacji arytmetycznych.Przyk ladowy pe lny program wyznaczaj ↪acy n-t ↪a liczb ↪e Fibonacciego (dla danego z kla-

wiatury n) mog lby wygl ↪adac nast ↪epuj ↪aco.

1 # include <iostream >2 # include <cstdlib >3 using namespace std;4

5 int pobierz_n ()6 {7 int n;8 do9 {

10 cout << "Podaj n: ";11 cin >> n;12 if (n < 0) {13 cout << " Niepoprawne dane! Spróbuj ponownie ." << endl;14 }15 while (n < 0);16

17 return n; // t u t a j na pewno n >= 018 }19

20 int fibiter (int n)21 {22 if (n <= 1) return 1; // t u w y n i k j e s t z n a n y23

24 int k = 1;25 int Fk_poprz = 1; // Fk−126 int Fk_teraz = 1; // Fk

27

28 do {29 int nast = Fk_teraz + Fk_poprz ; // p o l i c z k o l e j n y w y r a z30 k++;31

32 Fk_poprz = Fk_teraz ; // Fk−1 ( j u z d l a nowego k )33 Fk_teraz = nast; // Fk

34 }35 while (k <= n);36

37 return Fk_teraz ;38 }39

40

41 int main ()42 {

1Inny przyk lad: mimo ze rozwi ↪azanie uk ladu rownan postaci Ax = b mozemy poprawnie wyrazic zapomoc ↪a x = A−1b, nigdy implementacja komputerowa nie powinna odwracac macierzy wprost. Podykto-wane jest to tzw. stabilnosci ↪a numeryczn ↪a metody – innymi s lowy, z powodu niedoskona losci arytmetykizmiennoprzecinkowej komputera, taki sposob obliczen generuje cz ↪esto zbyt niedok ladny wynik.

Ostatnia aktualizacja: 1 pazdziernika 2016 r.

5.2. PODSTAWOWE ABSTRAKCYJNE TYPY DANYCH 7

43 int n = pobierz_n ();44 cout << "F(" << n << ") = " << fibiter (n) << endl;45 return 0;46 }

Zwrocmy uwag ↪e na podzia l programu na czytelne modu ly (tj. funkcje), z ktorych kazdys luzy do przeprowadzenia scisle okreslonej czynnosci.

5.2. Podstawowe abstrakcyjne typy danych

W tym podrozdziale omowimy cztery nast ↪epuj ↪ace abstrakcyjne typy danych:

1. stos (rozdz. 5.2.1),

2. kolejk ↪e (rozdz. 5.2.2),

3. kolejk ↪e priorytetow ↪a (rozdz. 5.2.3),

4. s lownik (rozdz. 5.2.4).

Zapami↪etaj

Abstrakcyjny typ danych (ATD) umozliwia przechowywanie i organizowanie w spe-cyficzny dla siebie sposob informacji danego, ustalonego typu.

Kazdy abstrakcyjny typ danych okreslony jest za pomoc ↪a:

1. dziedziny, tj. typu zmiennych, ktore mozna w nim przechowywac,2. w lasciwego sobie dopuszczalnego zbioru operacji, ktore mozna na nim wykonac.

Co wazne, definicje wszystkich abstrakcyjnych typow danych abstrahuj ↪a od sposobuich implementacji. Kazdy zaprezentowany przez nas ATD moze zostac zaprogramowanyna wiele — z punktu widzenia realizowanej funkcjonalnosci — rownowaznych sposobow.Jednakze owe implementacje b ↪ed ↪a roznic si ↪e m.in. efektywnosci ↪a obliczeniow ↪a i pami ↪e-ciow ↪a, ktore — jak wiemy — maj ↪a kluczowe znaczenie w praktyce. W trakcie kolejnychzaj ↪ec b ↪edziemy rzecz jasna starac si ↪e poszukiwac rozwi ↪azan optymalnych. Najprostsze,tablicowe implementacje, pozostawiamy jako cwiczenie.

Informacja

Dla uproszczenia umawiamy si ↪e, ze kazdy prezentowany abstrakcyjny typ danychb ↪edzie s luzy l do przechowywania pojedynczych wartosci ca lkowitych, tj. danych typuint.

Jako jeden z najbardziej przyjaznych matematykowi przyk ladow ATD mozna wymienicznany z teorii mnogosci zbior (u nas zwany s lownikiem, zob. rozdz. 5.2.4). Otoz zbior mozeprzechowywac rozne (odmienne) wartosci (zgodnie z umow ↪a — ca lkowite). Dopuszczalneoperacje wykonywane na nim to m.in. sprawdzenie, czy dany element znajduje si ↪e w zbio-rze, dodanie elementu do zbioru i usuni ↪ecie elementu ze zbioru. Zauwazmy, ze wszystkie

Ostatnia aktualizacja: 1 pazdziernika 2016 r.

5.2. PODSTAWOWE ABSTRAKCYJNE TYPY DANYCH 8

inne operacje teoriomnogosciowe (np. suma, iloczyn, roznica zbiorow, czy dope lnienie)mog ↪a zostac zrealizowane za pomoc ↪a trzech wymienionych (aczkolwiek w sposob dalekiod optymalnego pod wzgl ↪edem szybkosci dzia lania2).

Zadanie

Zastanow si ↪e, w jaki sposob mozna najprosciej i najefektywniej zaimplementowac ATD typuzbior (s lownik) o elementach z {0, 1, . . . , 100}, a w jaki o dowolnych elementach z Z z uzyciemtablicy. Jak ↪a pesymistyczn ↪a z lozonosc obliczeniow ↪a maj ↪a w kazdym przypadku Twoje imple-mentacje trzech wyzej wymienionych operacji? Czy da si ↪e je zrealizowac w lepszy sposob?

5.2.1. Stos

Stos (ang. stack) jest abstrakcyjnym typem danych typu LIFO (ang. last-in-first-out),ktory udost ↪epnia przechowywane wen elementy w kolejnosci odwrotnej niz ta, w ktorejby ly one zamieszczane.

Stos udost ↪epnia trzy operacje:

– push (umiesc) — wstawia element na stos,– pop (zdejmij) — usuwa i zwraca element ze stosu (LIFO!),– empty (czy pusty) — sprawdza, czy na stosie nie znajduj ↪a si ↪e zadne elementy.

Przyk lad uzycia stosu.

1 int main ()2 {3 Stos s = tworzStos ();4

5 push(s, 3);6 push(s, 7);7 push(s, 5);8

9 while (! empty(s))10 cout << pop(s);11 // w y p i s z e na e k r a n 57312

13 kasujStos (s);14

15 return 0;16 }

2Wi ↪ecej na ten temat dowiemy si ↪e z wyk ladu z Algorytmow i struktur danych. Zob. takze ksi ↪azk ↪eN. Wirtha

”Algorytmy + struktury danych = programy”, Wyd. WNT, Warszawa, 2001.

Ostatnia aktualizacja: 1 pazdziernika 2016 r.

5.2. PODSTAWOWE ABSTRAKCYJNE TYPY DANYCH 9

Zadanie

Zaimplementuj operacje wykorzystywane w powyzszej funkcji main(). Stos reprezentuj w po-staci struktury zawieraj ↪acej dynamicznie alokowan ↪a tablic ↪e, na przyk lad:

1 struct Stos {2 int∗ wartosci ;3 int n; // a k t u a l n y r o z m i a r t a b l i c y4 int g; // i n d e k s , , w i e r z c h o l k a ’ ’ s t o s u5 };

Zaproponuj rozne implementacje, m.in. przypadek, n zmienia si ↪e za kazdym razem, gdy wy-konujemy operacje push i pop oraz gdy n podwaja si ↪e, gdy dopiero zachodzi taka potrzeba.Jak ↪a z lozonosc obliczeniow ↪a maj ↪a ww. operacje w pesymistycznym, a jak ↪a w optymistycznymprzypadku?

5.2.2. Kolejka

Kolejka (ang. queue) to ATD typu FIFO (ang. first-in-first-out). Podstawow ↪a w lasnosci ↪akolejki jest to, ze pobieramy z niej elementy w takiej samej kolejnosci (a nie w odwrotnejjak w przypadku stosu), w jakiej by ly one umieszczane.

Kolejka udost ↪epnia nast ↪epuj ↪ace trzy operacje:

– enqueue (umiesc) — wstawia element do kolejki,– dequeue (wyjmij) — usuwa i zwraca element z kolejki (FIFO!),– empty (czy pusty).

Przyk lad uzycia kolejki.

1 int main ()2 {3 Kolejka k = tworzKolejke ();4

5 enqueue (k, 3);6 enqueue (k, 7);7 enqueue (k, 5);8

9 while (! empty(k))10 cout << dequeue (k);11 // w y p i s z e na e k r a n tym razem 37512

13 kasujKolejke (k);14

15 return 0;16 }

Zadanie

Zaimplementuj operacje wykorzystywane w powyzszej funkcji main(). Kolejk ↪e reprezentujw postaci struktury zawieraj ↪acej dynamicznie alokowan ↪a tablic ↪e. Jak ↪a z lozonosc obliczeniow ↪amaj ↪a ww. operacje w pesymistycznym, a jak ↪a w optymistycznym przypadku?

Ostatnia aktualizacja: 1 pazdziernika 2016 r.

5.2. PODSTAWOWE ABSTRAKCYJNE TYPY DANYCH 10

5.2.3. Kolejka priorytetowa

Kolejka priorytetowa (ang. priority queue) to abstrakcyjny typ danych typu LPF(ang. lowest-priority-first). Moze ona s luzyc do przechowywania elementow, na ktorychzosta la okreslona pewna relacja porz ↪adku liniowego <=3. Wydobywa si ↪e z niej elementypoczynaj ↪ac od tych, ktore maj ↪a najmniejszy priorytet (s ↪a minimalne wzgl ↪edem relacji <=).Jesli w kolejce priorytetowej znajduj ↪a si ↪e elementy o takich samych priorytetach, obowi ↪a-zuje dla nich kolejnosc FIFO, tak jak w przypadku zwyk lej kolejki.

Omawiany ATD udost ↪epnia trzy operacje:

– insert (w loz) — wstawia element na odpowiednie miejsce kolejki priorytetowej.– pull (pobierz) — usuwa i zwraca element minimalny,– empty (czy pusty).

Przyk lad uzycia kolejki priorytetowej.

1 int main ()2 {3 KolejkaPiorytetowa k = tworzKolejkePriorytetowa ();4

5 insert (k, 3);6 insert (k, 7);7 insert (k, 5);8

9 while (! empty(k))10 cout << pull(k);11 // w y p i s z e na e k r a n tym razem 35712

13 kasujKolejkePriorytetowa (k);14

15 return 0;16 }

Zadanie

Zaimplementuj operacje wykorzystywane w powyzszej funkcji main(). Kolejk ↪e priorytetow ↪areprezentuj w postaci struktury zawieraj ↪acej dynamicznie alokowan ↪a tablic ↪e. Jak ↪a z lozonoscobliczeniow ↪a maj ↪a ww. operacje w pesymistycznym, a jak ↪a w optymistycznym przypadku?

5.2.4. S lownik

S lownik (ang. dictionary) to abstrakcyjny typ danych, ktory udost ↪epnia trzy operacje:

– search (znajdz) — sprawdza, czy dany element znajduje si ↪e w s lowniku,– insert (dodaj) — dodaje element do s lownika, o ile taki juz si ↪e w nim nie znajduje,– remove (usun) — usuwa element ze s lownika.

Dla celow pomocniczych rozwazac mozemy takze nast ↪epuj ↪ace dzia lania na s lowniku:

– print (wypisz),– removeAll (usun wszystko).

3W naszym przypadku, jako ze umowilismy si ↪e, iz omawiane ATD przechowuj ↪a dane typu int, najcz ↪e-sciej b ↪edzie to po prostu zwyk ly porz ↪adek ¬.

Ostatnia aktualizacja: 1 pazdziernika 2016 r.

5.2. PODSTAWOWE ABSTRAKCYJNE TYPY DANYCH 11

Informacja

Czasem zak lada si ↪e, ze na przechowywanych elementach zosta la okreslona relacjaporz ↪adku liniowego <=. Wtedy mozemy tworzyc bardziej efektywne implementacjes lownika, np. z uzyciem drzew binarnych (zob. rozdz. 7) albo algorytmu wyszukiwaniabinarnego (po lowkowego) w przypadku tablic.

Pesymistyczna z lozonosc obliczeniowa algorytmu wyszukiwania binarnego wynosizaledwie O(log n) dla danej uporz ↪adkowanej tablicy t rozmiaru n. Metoda ta bazujena za lozeniu, ze poszukiwany element x, jesli oczywiscie znajduje si ↪e w tablicy, musibyc ulokowany na pozycji (indeksie) ze zbioru {l,l+1,...,p}. Na pocz ↪atku ustalamyoczywiscie l=0 i p=n−1. W kazdej kolejnej iteracji znajdujemy

”srodek” owego obszaru

poszukiwan, tzn. m=(p+l)/2 oraz sprawdzamy, jak ma si ↪e t[m] do x. Jesli okaze si ↪e, zejeszcze nie trafilismy na w lasciwe miejsce, to wtedy zaw ↪ezamy (o co najmniej po low ↪e)nasz obszar modyfikuj ↪ac odpowiednio wartosc l b ↪adz p.

1 bool search (int x, int∗ t, int n)2 {3 if (n <= 0) return false;4

5 // w y s z u k i w a n i e b i n a r n e ( po l owkowe )6 int l = 0; // p o c z ↪a t k o w y o b s z a r p o s z u k i w a n7 int p = n−1; // − c a l a t a b l i c a8 while (l <= p)9 {

10 int m = (p+l)/2; // ” s r o d e k ” o b s z a r u p o s z u k i w a n11

12 if (x == t[m]) return true; // z n a l e z i o n y − k o n i e c13 else if (x > t[m]) l = m+1; // zaw ↪ezamy o b s z a r p o s z u k i w a n14 else p = m−1; // jw .15 }16

17 return false; // n i e z n a l e z i o n y18 }

Powyzsz ↪a funkcj ↪e latwo zmodyfikowac, by zwraca la indeks, pod ktorym znajduje si ↪eznaleziony element b ↪adz jak ↪as wyroznion ↪a wartosc (np. −1), gdy elementu nie maw tablicy (potrafisz?). Sprobuj takze samodzielnie zaimplementowac rekurencyjn ↪awersj ↪e tego algorytmu.

Przyk lad uzycia s lownika.

1 int main ()2 {3 Slownik s = tworzSlownik ();4

5 int i, j;6 cout << "Podaj wartości , które maja się znaleźć w słowniku . ";7 cout << "Wpisz wartość ujemną , by zakończyć wstawianie . ";8

9 cin >> i;10 while (i >= 0) {11 insert (s, i);12 cin >> i;13 }14

Ostatnia aktualizacja: 1 pazdziernika 2016 r.

5.2. PODSTAWOWE ABSTRAKCYJNE TYPY DANYCH 12

15 cout << "Podaj wartości , które chcesz znaleźć w słowniku . ";16 cout << "Wpisz wartość ujemną , by zakończyć wyszukiwanie . ";17

18 cin >> i;19 while (i >= 0) {20 if ( search (s, i)) {21 cout << "Jest!" << endl;22 cout << " Usunąć ? 0 == NIE." << endl;23 cin >> j;24 if (j != 0)25 remove (s, i);26 }27 else28 cout << "Nie ma!" << endl;29 cin >> i;30 }31

32 kasujSlownik (s);33

34 return 0;35 }

Ostatnia aktualizacja: 1 pazdziernika 2016 r.

5.3. CWICZENIA 13

5.3. Cwiczenia

Zadanie 5.1. Zdefiniuj struktur ↪e Wektor2d o dwoch sk ladowych typu double. Utworzfunkcj ↪e dlugosc(), ktora dla danego obiektu typu Wektor2d zwraca jego norm ↪e eukli-desow ↪a. Dalej, stworz funkcj ↪e inssort(), ktora implementuje algorytm sortowania przezwstawianie danej tablicy o elementach typu Wektor2d* (zamian ↪e elementow implemen-tujemy wprost na wskaznikach). Napisz tez funkcj ↪e main(), w ktorej wszystko zostaniedok ladnie przetestowane.

Zadanie 5.2. Napisz funkcj ↪e horners(), ktora wyznacza wartosci wszystkich k poda-nych wielomianow w podanym punkcie. Wielomian reprezentujemy przy uzyciu struk-tury Wielomian (sk ladaj ↪acej si ↪e z tablicy w o elementach typu double oraz stopnia wie-lomianu n). Deklaracja funkcji do napisania: double* horners(Wielomian* W, int k,double x).

Zadanie 5.3 (MD). Zdefiniuj struktur ↪e Zbior, ktora reprezentuje podzbior zbioru liczbnaturalnych (pola: tablica e o elementach typu int oraz liczba elementow n).

Napisz funkcj ↪e void dodaj_element(Zbior& zb, int e), ktora doda do wskazanegozbioru element e (jezeli element juz znajduje si ↪e w zbiorze, funkcja nie powinna robic nic).

Ponadto napisz funkcj ↪e Zbior przeciecie(Zbior* zbiory, int k), ktora wyznaczyprzeci ↪ecie wszystkich zbiorow zadanych w postaci k-elementowej tablicy zbiory, a takzeZbior suma(Zbior* zbiory, int k), ktora oblicza sum ↪e podanych zbiorow.

Zadanie 5.4 (MB). Zdefiniuj struktur ↪e Ulamek o dwoch sk ladowych licznik, mianowniktypu int – reprezentuje ona liczb ↪e wymiern ↪a. Napisz funkcje s luz ↪ace do dodawania, odej-mowania, mnozenia i dzielenia u lamkow, kazd ↪a postaci Ulamek nazwa_funkcji(Ulamekx, Ulamek y). Po wykonaniu kazdej z operacji u lamki powinny byc odpowiednio znormali-zowane; patrz algorytm NWD (zalecana metoda: przez dodatkow ↪a funkcj ↪e normalizuj()).

Nast ↪epnie napisz funkcje, ktore maj ↪ac dane tablice u lamkow wyznacz ↪a ich skumulo-wane minimum, maksimum, sum ↪e i iloczyn. Napisz tez funkcje, ktore wyznaczaj ↪a sum ↪e,roznic ↪e, iloczyn i iloraz u lamkow z dwoch zadanych tablic (odpowiednio zwektoryzowane).Dodatkowo, mozesz stworzyc funkcj ↪e, ktora sortuje u lamki wzgl ↪edem ich wartosci orazwypisuje u lamki na ekranie.

Zadanie 5.5 (MB). Dana jest struktura zdefiniowana jako struct Naukowiec {int*cytowania; int n; };. Zak ladamy, ze cytowania s ↪a zawsze posortowane nierosn ↪aco.

Napisz funkcj ↪e, ktora dodaje now ↪a publikacj ↪e z podan ↪a liczb ↪a cytowan do tablicycytowania, np. dla tablicy (5, 4, 4, 1) po dodaniu 3 otrzymujemy (5, 4, 4, 3, 1) (zwracamyuwag ↪e na poprawn ↪a alokacj ↪e nowej tablicy oraz dealokacj ↪e starej).

Utworz takze funkcj ↪e h(), ktora wyznacza indeks Hirscha danego naukowca.Dodatkowo, zaimplementuj nast ↪epuj ↪ace funkcje, ktore na wejsciu otrzymuj ↪a tablic ↪e

naukowcow: (a) wyznaczanie naukowca o najwi ↪ekszym indeksie Hirscha, (b) sortowanienaukowcow pod wzgl ↪edem indeksu h (od najlepszego do najgorszego).

Ostatnia aktualizacja: 1 pazdziernika 2016 r.