plik zawiera materiał ilustracyjny do wykładuics.p.lodz.pl/~stokfi/aisd/aisd_1.pdf · 3...

22
1 Plik zawiera materiał ilustracyjny do wykładu : (szczegółowe omówienie zagadnień z przykładami podane będzie w trakcie wykładu) Efekty kształcenia : 1. Nabycie teoretycznej i praktycznej umiejętności posługiwania się złożonymi strukturami danych. 2. Nabycie umiejętności rozwiązywanie problemów poprzez poprawną algorytmizację. 3. Nabycie umiejętności oceny klasy algorytmów z podstawami szacowania ich złożoności obliczeniowej. Program ramowy przedmiotu : 1. Pojęcie algorytm. Postacie i własności algorytmów. Przetwarzanie imperatywne. Asercja początkowa i końcowa algorytmu. Podstawowe metody strukturalizacji algorytmów: rozgałęzienia warunkowe, pętle iteracyjne, rekurencja. Warunek stopu. Pseudokod. 2. Przykłady algorytmów numerycznych. Poszukiwanie miejsc zerowych funkcji. Obliczanie przybliżonej wartości całki oznaczonej. Schemat konstruowania poprawnych algorytmów. 3. Typy danych. Własności typów pierwotnych, złożonych i abstrakcyjnych. Zmienne dynamiczne i nie dynamiczne. 4. Tablica i rekord jako agregaty danych. Alokacja w pamięci. Bezpośredni dostęp do składowych. 5. Podstawowe algorytmy i ich cechy. Wyszukiwanie liniowe i binarne. Przykładowe algorytmy sortowania Nazwa przedmiotu: Algorytmy i struktury danych Egz. Liczba godzin: 30 godz. wykładu + 30 godz. ćwiczeń audytoryjnych

Upload: vothuy

Post on 01-Mar-2019

218 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Plik zawiera materiał ilustracyjny do wykładuics.p.lodz.pl/~stokfi/aisd/aisd_1.pdf · 3 Literatura uzupełniająca: 1. Drozdek A.: C++ Algorytmy i struktury danych, Helion, 2004

1

Plik zawiera materiał ilustracyjny do wykładu: (szczegółowe omówienie zagadnień z przykładami podane będzie w trakcie wykładu)

Efekty kształcenia:

1. Nabycie teoretycznej i praktycznej umiejętności posługiwania się złożonymi strukturami danych.

2. Nabycie umiejętności rozwiązywanie problemów poprzez poprawną algorytmizację.

3. Nabycie umiejętności oceny klasy algorytmów z podstawami szacowania ich złożoności obliczeniowej.

Program ramowy przedmiotu: 1. Pojęcie algorytm. Postacie i własności algorytmów.

Przetwarzanie imperatywne. Asercja początkowa i końcowa algorytmu. Podstawowe metody strukturalizacji algorytmów: rozgałęzienia warunkowe, pętle iteracyjne, rekurencja. Warunek stopu. Pseudokod.

2. Przykłady algorytmów numerycznych. Poszukiwanie miejsc zerowych funkcji. Obliczanie przybliżonej wartości całki oznaczonej. Schemat konstruowania poprawnych algorytmów.

3. Typy danych. Własności typów pierwotnych, złożonych i abstrakcyjnych. Zmienne dynamiczne i nie dynamiczne.

4. Tablica i rekord jako agregaty danych. Alokacja w pamięci. Bezpośredni dostęp do składowych.

5. Podstawowe algorytmy i ich cechy. Wyszukiwanie liniowe i binarne. Przykładowe algorytmy sortowania

Nazwa przedmiotu: Algorytmy i struktury danych Egz. Liczba godzin: 30 godz. wykładu + 30 godz. ćwiczeń audytoryjnych

Page 2: Plik zawiera materiał ilustracyjny do wykładuics.p.lodz.pl/~stokfi/aisd/aisd_1.pdf · 3 Literatura uzupełniająca: 1. Drozdek A.: C++ Algorytmy i struktury danych, Helion, 2004

2

tablic: sortowanie przez proste wstawianie, sortowanie przez prostą zamianę (bąbelkowe), sortowanie przez podział (QuickSort). Cechy algorytmów sortowania. Metoda ”dziel i zwyciężaj”. Tablica indeksowa.

6. Algorytmy rekurencyjne. Przykłady. Stos dla zmiennych w rekurencji. Rekurencja zagnieżdżona. Kłopoty z rekurencją. Rekurencja a iteracja. Rekurencyjne typy danych.

7. Typ wskaźnikowy. Alokacja dynamiczna i nie dynamiczna zmiennych. Programowanie z wykorzystaniem struktur dynamicznych.

8. Listy liniowe. Algorytmy obsługi list liniowych. Dynamiczne LIFO-stosy i FIFO-kolejki, listy dwukierunkowe, samoorganizujące się listy. Listy z przeskokami. Kodowanie mieszające.

9. Drzewa i lasy. Drzewa binarne. Drzewo binarnych poszukiwań. Drzewo zrównoważone i drzewo AVL. Drzewa decyzyjne. Wyrażenia kropkowe.

10. Grafy. Metody reprezentacji grafu. Algorytm szukania w głąb dla grafu. Drzewa rozpinające graf.

10.Algorytmy z powrotami. Metody usprawniania algorytmów żarłocznych: systematyczne, heurystyczne. 11.Szacowanie złożoności obliczeniowej algorytmów iteracyjnych. Klasy P- i NP-problemów. Problemy NP- zupełne i silnie NP-zupełne.

Wykład ma charakter autorski. Literatura podstawowa:

1. Wróblewski P.: Algorytmy, struktury danych i techniki programowania, Helion, 2003

2. Kotowski P: Algorytmy + struktury danych = abstrakcyjne typy danych, Wydawnictwo BTC, 2006

Page 3: Plik zawiera materiał ilustracyjny do wykładuics.p.lodz.pl/~stokfi/aisd/aisd_1.pdf · 3 Literatura uzupełniająca: 1. Drozdek A.: C++ Algorytmy i struktury danych, Helion, 2004

3

Literatura uzupełniająca: 1. Drozdek A.: C++ Algorytmy i struktury danych, Helion,

2004 2. Aho V., Ullman J. D.: Wykłady z informatyki z

przykładami w języku C, Helion, 2003 3. Aho V., Hopcroft J. E., Ullman J. D.: Projektowanie i

analiza algorytmów, Helion, 2003 4. Harris S., Ross J.: Od podstaw Algorytmy, WNT, Helion,

2006 5. Neapolitan R., Naimipour K.: Podstawy algorytmów z

przykładami w języku C++, Helion, 2004

1. Pojęcie algorytmu Przez algorytm rozumieć będziemy, składający się z ciągu

kroków, przepis na otrzymanie jakiegoś konkretnego produktu, niekoniecznie materialnego. Przykładami algorytmów, spotykanych na co dzień, może być: przepis kulinarny, albo opis przebiegu czynności przyjęcia na studia. W informatyce najczęściej mamy do czynienia z tak

zwanymi algorytmami imperatywnymi (od greckiego słowa impero – rozkazywać). Schemat przetwarzania imperatyw-nego przedstawia rys. 1.

Rys. 1. Schemat przetwarzania imperatywnego Przetwarzanie imperatywne charakteryzuje się tym, że algorytm pobiera pewne dane wejściowe i przetwarza je na dane wyjściowe.

Dane wejściowe

Algorytm Dane wyjściowe

asercja początkowa asercja końcowa

Page 4: Plik zawiera materiał ilustracyjny do wykładuics.p.lodz.pl/~stokfi/aisd/aisd_1.pdf · 3 Literatura uzupełniająca: 1. Drozdek A.: C++ Algorytmy i struktury danych, Helion, 2004

4

Nie jest to jedyny sposób przetwarzania informacji. Na uwagę zasługuje przetwarzanie deklaratywne. W tym przypadku ”pobieranie danych” sprowadza się do kompletowania zbioru zależności (przesłanek). Natomiast działanie algorytmu polega na wykazaniu prawdziwości pewnego stwierdzenia (celu) opisanego przez asercję końcową. W przetwarzaniu imperatywnym asercja początkowa określa, jakie warunki muszą spełniać dane wejściowe, aby algorytm (program) wykonywał się poprawnie (zgodnie z założeniami) w całym zestawie opisanych przez asercję początkową danych wejściowych. Podobnie – asercja końcowa określa, jakie warunki spełniają poszczególne zestawy danych wyjściowych, przy założeniu, że asercja początkowa jest spełniona, a więc algorytm działa poprawnie w całym zestawie danych wejściowych.

Określenie zbiorów danych: wejściowych i wyjściowych, jak również podanie pełnej postaci asercji: początkowej i końco-wej, należy do obowiązków tworzącego algorytm. Musimy mieć świadomość, że przetwarzane dane (zarówno wejściowe, jak i wyjściowe) mogą mieć różny charakter. Mogą to być dane: numeryczne (liczbowe), napisowe, w formie plików graficznych, muzycznych, itp. Spotykane postacie algorytmów: schemat blokowy, opis słowny, pseudokod, zapis w języku programowania (program).

Wszystkie te postaci algorytmu są tylko jego zewnętrzną formą, są więc semantycznie równoważne.

Page 5: Plik zawiera materiał ilustracyjny do wykładuics.p.lodz.pl/~stokfi/aisd/aisd_1.pdf · 3 Literatura uzupełniająca: 1. Drozdek A.: C++ Algorytmy i struktury danych, Helion, 2004

5

Cechy, jakie powinien posiadać algorytm: określoność, jednoznaczność, skończoność.

Określoność algorytmu sprowadza się do używania w jego opisie wyłącznie pojęć, których znaczenie w danej dziedzinie problemu jest ściśle i jednoznacznie określone. Jednoznaczność algorytmu dotyczy tak zwanych punktów węzłowych, w których zmuszeni jesteśmy podjąć decyzję dotyczącą dalszego przetwarzania danych. a) b) Rys. 2. Problem niejednoznaczność algorytmu: a) algorytm z punktem węzłowym W prowadzącym do niejednoznaczności, b) rozwiązanie tego problemu poprzez umieszczenie w punkcie niejednoznaczności warunku logicznego Skończoność algorytmu wymaga, aby dla każdego zestawu danych wejściowych, spełniającego asercję początkową, algorytm doszedł do punktu końcowego (zakończył się), osiągając zestaw wyników, spełniający asercje końcową. Mogą być dwie przyczyny nie dojścia algorytmu do punktu końcowego: zawieszenie przetwarzania, zapętlenie.

A

B B

A W W

Page 6: Plik zawiera materiał ilustracyjny do wykładuics.p.lodz.pl/~stokfi/aisd/aisd_1.pdf · 3 Literatura uzupełniająca: 1. Drozdek A.: C++ Algorytmy i struktury danych, Helion, 2004

6

2. Podstawowe metody strukturalizacji algorytmów

2.1. Pętle iteracyjne Przez proces iteracyjny rozumieć będziemy kontynuowanie powtórzeń wskazanego ciągu kroków algorytmu dowolną (teoretycznie nieskończoną) ilość razy. Podstawowymi schematami realizacji tego procesu są pętla while i pętla repeat.

Szczegółowe omówienie działania pętli podawane jest w trakcie wykładu. P P I fałsz W fałsz prawda W prawda I a) b) while ( W ) I ; do { I } while ( W ) ; Rys. 3. Pętle iteracyjne: a) pętla while b) pętla repeat Można wykazać, że obie pętle są równoważne . Wybór jednej z nich jest podyktowany najczęściej wygodą zapisu. Zasadnicze znaczenie przy tworzeniu algorytmów z użyciem pętli iteracyjnych ma zapewnienie warunku stopu, to jest

Page 7: Plik zawiera materiał ilustracyjny do wykładuics.p.lodz.pl/~stokfi/aisd/aisd_1.pdf · 3 Literatura uzupełniająca: 1. Drozdek A.: C++ Algorytmy i struktury danych, Helion, 2004

7

niedopuszczenie do zapętlenia się algorytmu. Można go sformułować jak niżej Warunek stopu pętli iteracyjnej:

Warunek W i ciąg instrukcji I muszą być tak skonstruowane, aby dla każdego zestawu danych, jaki jest dopuszczalny w punkcie P algorytmu, wykonanie pętli mogło być zakończone po wykonaniu skończonej ilości powtórzeń. A necessary condition of so formulating demand is the assurance of the influence of the instruction I on variables, entering into the expression W. This influence must assure, after the realization of the finite course of repetitions, the bringing to the falsity of the condition W. Oprócz wyżej wymienionych, stosunkowo często używaną jest pętla for. Jest ona modyfikacją pętli while, w której użyto zmiennej sterującej pętlą. Zmienna sterująca musi być typu porządkowego. Zmienne typu porządkowego mają tę własność, że dla każdej wartości tego typu (za wyjątkiem pierwszej i ostatniej ograniczonego zbioru wartości) możemy zawsze określić wartość poprzednią i następną. Zmiennej sterującej przypisuje się jej jednorazowo, przed wejściem do pętli, pewną wartość początkową. Wartość ta jest następnie, przy każdym nawrocie, monotonicznie zmieniana (zwiększana, lub zmniejszana). W pętli for warunek W określa, do jakiej wartości (największej, lub odpowiednio - najmniejszej) wartość zmiennej sterującej ma prawo się zwiększyć (lub odpowiednio – zmniejszyć), wyznaczając tym samym ilość nawrotów w pętli iteracyjnej.

for(nadanie wartości początkowej; warunek stopu; instrukcja zwiększająca) instrukcja wewnętrzna cyklu; (a)

Page 8: Plik zawiera materiał ilustracyjny do wykładuics.p.lodz.pl/~stokfi/aisd/aisd_1.pdf · 3 Literatura uzupełniająca: 1. Drozdek A.: C++ Algorytmy i struktury danych, Helion, 2004

8

for(int i=1; i<=N; i++) I; (b)

Rys. 4. (a) - postać ogólna pętli for, (b) – przykład zapisu pętli for w języku C. Tutaj: i jest zmienna sterującą typu całkowitego, N jest pewną stałą całkowitą, I jest instrukcją wewnętrzną cyklu.

2.2. Rekurencja Zjawisko rekurencji inaczej zwane rekursją, jest naturalnym zjawiskiem spotykanym powszechnie. Jego istotą jest odwoływanie się do samego siebie. Algorytmy rekurencyjne przerywają więc w trakcie przetwarzania swój wewnętrzny, sekwencyjny proces obliczeniowy, po czym wznawiają nowy proces rozpoczynający działanie algorytmu od początku, pozostawiając dokończenie poprzedniego procesu na okres późniejszy. W ten sposób powstaje teoretycznie nieskończony, ciąg wywołań rekurencyjnych algorytmu, który aby mógł się zakończyć wymaga spełnienia warunku stopu dla rekurencji. Rekurencja jest kolejną, obok procesu iteracyjnego, ważną metodą strukturalizacji algorytmów. Rys. 5. Ogólny schemat przetwarzania rekurencyjnego

2.3. Pseudokod

algorytm rekurencyjny

polecenia algorytmu

polecenie wywołania rekurencyjnego

Page 9: Plik zawiera materiał ilustracyjny do wykładuics.p.lodz.pl/~stokfi/aisd/aisd_1.pdf · 3 Literatura uzupełniająca: 1. Drozdek A.: C++ Algorytmy i struktury danych, Helion, 2004

9

Pseudokod jest to sposób zapisu algorytmu z wykorzysta-niem metod strukturalizacji języka programowania, ale bez dopracowywania szczegółów. Część algorytmu zapisujemy więc w języku naturalnym. Taki sposób zapisu algorytmu pozwala spojrzeć na niego z ogólnego poziomu, bez gubienia się w szczegółach. Po akceptacji ogólnej struktury algorytmu, możemy doprecyzować szczegóły w wybranym języku programowania.

do { wybierz następnego kandydata; if(dopuszczalny) zapisz go; else usuń go; } while(nie znaleziono rozwiązania);

Rys. 6. Przykładowy fragment algorytmu z powrotami zapisanego w pseudokodzie 3. Przykłady algorytmów numerycznych Obliczenia numeryczne wykonywane są na danych w postaci liczb. W początkach rozwoju informatyki były to pierwsze i jedyne zastosowanie komputerów. Dzięki możliwości wykonywania bardzo szybkich obliczeń można było rozwiązywać w sposób przybliżony problemy, które przedtem wymagały poszukiwań rozwiązania na drodze analitycznej. Głównym celem tego rozdziału jest pokazanie, jak poprawnie konstruować proste algorytmy, wykorzystując procesy iteracyjne.

3.1. Poszukiwanie miejsc zerowych funkcji

Page 10: Plik zawiera materiał ilustracyjny do wykładuics.p.lodz.pl/~stokfi/aisd/aisd_1.pdf · 3 Literatura uzupełniająca: 1. Drozdek A.: C++ Algorytmy i struktury danych, Helion, 2004

10

Zadanie 1 Zapisać algorytm znajdujący w zadanym domkniętym przedziale <a,b> punkt przecięcia krzywej f(x) z osią Ox. Mówiąc inaczej znaleźć rozwiązanie równania f(x)=0 w zadanym przedziale <a,b>. Dla uproszczenia założymy, że w zadanym przedziale występuje tylko jeden taki punkt. Przyjmijmy, że algorytm konstruować będziemy według pewnego schemat postępowania, zapewniający tworzenie poprawnych, dobrze zbudowanych, algorytmów:

1. Mieć dobry pomysł na rozwiązanie postawionego problemu.

2. Określić dane wejściowe i wyjściowe algorytmu (w szczególności rodzaj danych i ich typy).

3. Określić warunki, jakie spełniają dane wejściowe i wyjściowe (asercja początkowa i końcowa algorytmu).

4. Zapisać algorytm w wybranej (dowolnej) formie. Jeżeli algorytm zawiera pętle iteracyjne, zbadać warunek stopu. Punkt ten kończy etap syntezy algorytmu.

5. Dokonać formalnej analizy zapisanego algorytmu pod kątem jego poprawności. To znaczy wykazać, że pobierając dane spełniające warunki początkowe, algorytm produkuje oczekiwane, spełniające ściśle warunki końcowe, wyniki, oraz że jest skończony w całym zakresie danych wejściowych?

6. Rozważyć możliwość optymalizacji algorytmu ze względu na wykorzystanie pamięci, jak również czasu jego wykonania.

Ad. 1

Page 11: Plik zawiera materiał ilustracyjny do wykładuics.p.lodz.pl/~stokfi/aisd/aisd_1.pdf · 3 Literatura uzupełniająca: 1. Drozdek A.: C++ Algorytmy i struktury danych, Helion, 2004

11

Zastosujemy metodę dzielenia przedziału, w którym znajduje się rozwiązanie, na dwie równe części. Do dalszych poszukiwań wybieramy tę połówkę, której krańce mają różne znaki. Czynności te będziemy iteracyjnie powtarzać, aż do momentu, w którym przedział otaczający rozwiązanie stanie się mniejszy od pewnej zadanej, bardzo małej wartości ε. y a p b

x Rys. 7. Metoda połówkowa (bisekcji) Ad. 2 Danymi są trzy liczby typu zmiennopozycyjnego a i b, oraz ε. Wynik jest pojedynczą, zmiennopozycyjną wartością, leżącą wewnątrz domkniętego przedziału <a,b>. Ad. 3 Asercję początkową i końcową zapisano w formie komentarza. Założono, że zachodzi b>a. Ponadto ε jest wartością dodatnią, dostatecznie małą wobec długości przedziału poszukiwań, np. ε=0.0001. Ad. 4

Page 12: Plik zawiera materiał ilustracyjny do wykładuics.p.lodz.pl/~stokfi/aisd/aisd_1.pdf · 3 Literatura uzupełniająca: 1. Drozdek A.: C++ Algorytmy i struktury danych, Helion, 2004

12

Konstruując iteracyjny algorytm posłużymy się pętlą do{…}while, ponieważ aby móc sensownie badać warunek zakończenia obliczeń, należy przynajmniej jeden raz wykonać operacje dzielenia przedziału poszukiwań na pół, z następującą po niej operacją zawężenia przedziału. Poniżej finalny zapis algorytmu:

wprowadź: a, b; // liczby zmiennopozycyjne, b>a wprowadź: eps; // dodatnia liczba zmiennopozycyjna // dostatecznie mała względem b-a do { p=(a+b)/2; if(f(a)*f(p)<0) b=p; else a=p; } while((b-a)>eps); zwróć: (a+b)/2; //wartość leżąca bardzo blisko rozwiązania Badanie warunku stopu dla pętli iteracyjnej: Ponieważ b>a pętla wykona się poprawnie przynajmniej jeden raz. Każdorazowe wykonanie wnętrza pętli zawęża przedział przesuwając punkt b w lewo, lub punkt a w prawo. Takie zawężanie przedziału poszukiwań musi w skończonej ilości powtórzeń doprowadzić do sytuacji, w której warunek (b-a)>eps stanie się nieprawdziwy. Mogą jednak wystąpić trudne do przewidzenia problemy, jeśli długość przedziału poszukiwań, lub wartość eps będzie nadzwyczaj mała (na granicy możliwości zapisania liczby w pamięci komputera). Ad. 5 Z przeprowadzonej w punkcie 4 analizy wynika, że algorytm będzie działał poprawnie dla każdego zestawu danych, spełniających asercje początkową. Nie występują też warunki mogące powodować zawieszenie przetwarzania.

Page 13: Plik zawiera materiał ilustracyjny do wykładuics.p.lodz.pl/~stokfi/aisd/aisd_1.pdf · 3 Literatura uzupełniająca: 1. Drozdek A.: C++ Algorytmy i struktury danych, Helion, 2004

13

Ad. 6 Brak możliwości optymalizacji algorytmu, opartego na zastosowanym pomyśle, chociaż znane jest wiele innych rozwiązań tego problemu, opartych jednak na innych pomysłach. Musimy mieć świadomość, że jakkolwiek zaproponowany powyżej schemat postępowania był ciągiem kolejnych kroków, to w rzeczywistości procesowi konstruowania algo-rytmu towarzyszą zwykle nawroty do kroków poprzednich w celu ich doprecyzowania. Może też być trochę inna kolejność wykonywania poszczególnych kroków. Wynika to zawsze z charakteru rozwiązywanego problemu.

3.2. Obliczanie przybliżonej wartości całki oznaczonej

Zadanie 2 Zapisać algorytm znajdujący przybliżoną wartość całki oznaczonej

b

a

dxxfI )(

Gdzie funkcja podcałkowa f(x) jest nieujemna w całym przedziale całkowania <a,b>. Ad. 1 Zgodnie z interpretacją geometryczną, wartość całki jest równa polu pod krzywą f(x). f(x)

Page 14: Plik zawiera materiał ilustracyjny do wykładuics.p.lodz.pl/~stokfi/aisd/aisd_1.pdf · 3 Literatura uzupełniająca: 1. Drozdek A.: C++ Algorytmy i struktury danych, Helion, 2004

14

a b x

x0 x1 x2 x3 . . . xn =b Rys. 7. Przybliżone całkowanie funkcji (metoda trapezów) Zastosujemy metodę polegająca na przybliżeniu pola pod krzywą przez sumę pól trapezów. Dzieląc przedział całkowa-nia o długości b-a na n równych odcinków o długości (b-a)/n zastąpimy w sposób przybliżony pole po krzywą sumą trapezów. Oczywiście im wartość n będzie większa, tym otrzymany wynik będzie bliższy wartości oczekiwanej. Wyprowadzenie wzoru do obliczeń:

1

0

11

0*

2)()(N

i

iiN

ii hxfxfPI

1

0

))*(2

)()((*N

ihiafbfafh

wprowadź: a, b; // liczby zmiennopozycyjne, b>a wprowadź: n; // liczba całkowita, n>0 h=(b-a)/n; wynik=(f(a)+f(b))/2; for(int i=1; i<n; i++) wynik+=f(a+i*h); zwróć: wynik*h; // przybliżona wartość całki oznaczonej Badanie poprawności algorytmu pozostawiamy czytelnikowi.

Page 15: Plik zawiera materiał ilustracyjny do wykładuics.p.lodz.pl/~stokfi/aisd/aisd_1.pdf · 3 Literatura uzupełniająca: 1. Drozdek A.: C++ Algorytmy i struktury danych, Helion, 2004

15

4. Typy danych Języki programowania posługują się pojęciem typu danej. Pojęcie to zdefiniować można w różny sposób. Jednak z prak-tycznego punktu widzenia: Rozmiar pamięci zajmowanej przez daną określanego

typu (wyrażony w bajtach) jest zawsze ściśle określony. Określony jest również sposób implementacji, czyli

sposób przechowywania danej w pamięci komputera, Określony jest też zestaw operacji, jakie można wykonać

na danej, chociaż może zależeć on od języka programow-ania.

Typy danych podzielić można na: pierwotne, złożone i abstrakcyjne.

4.1. Typy pierwotne

Typ pierwotny to taki, którego w danym języku nie da się zdefiniować za pomocą innych typów. Większość języków posiada pewien zestaw typów pierwotnych: char, int, float, boolean, string. Niektóre własności typów pierwotnych: Typy zmiennopozycyjne (float, double) to obecnie

prawie zawsze typy obsługiwane sprzętowo, zgodne ze standardem IEEE 754,

Typy znakowe (char, character) przez długie lata wykorzystywały kodowanie ASCII; obecnie coraz częściej używany jest Unicode,

Typ logiczny (boolean) jest kodowany za pomocą pojedynczego bitu (co jest oszczędne pamięciowo, ale wolniejsze w dostępie), lub całych bajtów,

Typ napisowy może być typem pierwotnym (tak jest w Javie – klasa String), lub też być szczególnym rodzajem tablicy (jak w języku C),

Page 16: Plik zawiera materiał ilustracyjny do wykładuics.p.lodz.pl/~stokfi/aisd/aisd_1.pdf · 3 Literatura uzupełniająca: 1. Drozdek A.: C++ Algorytmy i struktury danych, Helion, 2004

16

Występują również pierwotne typy stałopozycyjne (zwykle zwane decimal), czyli liczby z ustaloną w deklaracji liczbą cyfr i liczbą miejsc po przecinku. Typy takie dobrze nadają się do obliczeń finansowych.

4.2. Typy złożone

Dana typu złożonego zawiera wiele danych tego samego, lub

różnych typów pierwotnych, lub złożonych.

Aby określić typ złożony należy podać: typy jego składowych (tzw. typy bazowe), metodę strukturalizacji (połączenie składowych w

całość). Podstawowymi typami złożonymi są: tablica i rekord.

Tablica

Opis typu Dostęp do składowych Dane alokowane w pamięci operacyjnej, położone w kolejnych obszarach pamięci, wszystkie tego samego typu

Bezpośredni, poprzez wartość indeksu, lub wartość wskazania

Rekord

Opis typu Dostęp do składowych Dane alokowane w pamięci operacyjnej, położone w kolejnych obszarach pamięci różnych typów

Bezpośredni, poprzez nazwę pola rekordu

Własności podstawowych typów złożonych języka C++. Oba, wyżej wymienione, typy podstawowe zostaną omówione bardziej szczegółowo w dalszej części wykładu.

4.3. Abstrakcyjne typy danych

Page 17: Plik zawiera materiał ilustracyjny do wykładuics.p.lodz.pl/~stokfi/aisd/aisd_1.pdf · 3 Literatura uzupełniająca: 1. Drozdek A.: C++ Algorytmy i struktury danych, Helion, 2004

17

Abstrakcyjne typy danych pozwalają opisywać własności pewnych grup obiektów jako jedną całość. Opis ten obejmuje zarówno wspólne cechy obiektów, dające się wyrazić za pomocą danych, jak również funkcje służące manipulacji na tych danych, wspólne dla całej grupy obiektów. Tak rozumiany abstrakcyjny typ danych w językach zwanych obiektowymi, nazywamy klasą (Rys. 8). Klasa jest typem definiowanym przez użytkownika. W klasie, oprócz pól składowych definiuje się funkcje, zwane metodami, lub usługami. Są one uprawnione do bezpoś-redniego wykonywania operacji na polach obiektu danej klasy, które to pola są zwykle prywatne i niedostępne z zewnątrz klasy. ( parametry, dane składowe, pola) (usługi, funkcje składowe) Rys. 8. Schemat ogólny typu obiektowego (klasy)

4.4. Zmienne dynamiczne i nie dynamiczne

Każda zmienna, zarówno pierwotna, złożona, jak i typu abstrakcyjnego (to jest obiekt klasy) może być powołana do życia jako zmienna nie dynamiczna lub zmienna dynamiczna. Mówiąc najbardziej ogólnie, istotna różnica między nimi polega na tym, że o ile dostęp do zmiennych nie dynamicznych uzyskujemy posługując się ich nazwami, o tyle dostęp do zmiennych dynamicznych uzyskujemy wyłącznie

Nazwa klasy Atrybuty klasy Metody klasy

Page 18: Plik zawiera materiał ilustracyjny do wykładuics.p.lodz.pl/~stokfi/aisd/aisd_1.pdf · 3 Literatura uzupełniająca: 1. Drozdek A.: C++ Algorytmy i struktury danych, Helion, 2004

18

poprzez podanie ich adresu (położenia w pamięci operacyjnej). Oprócz wyżej wymienionych, w trakcie niniejszego wykładu

omawiane będą jeszcze poniższe struktury danych: listy liniowe jedno- i dwukierunkowe, stosy i kolejki, drzewa binarne i wielokierunkowe, grafy. Wyżej wymienione struktury, zwłaszcza powołane do życia

jako dynamiczne, odgrywają zasadniczą rolę w informatyce, dlatego właśnie im poświęcona będzie większa część wykładu. 5. Typ tablicowy Jak to już zostało powiedziane, tablice są złożonymi

strukturami danych przechowującymi pewną ilość danych tego samego typu ( typu bazowego). Typem bazowym może być dowolny typ danych (pierwotny, złożony, lub abstrakcyjny), nie zawierający w swej strukturze, na dowolnym poziomie, typów alokowanych poza pamięcią operacyjną (takich jak pliki czy strumienie). W trakcie realizacji programu tablice są przechowywane w pamięci operacyjnej komputera. Przykład deklaracji tablicy w języku C/C++:

int t [ N ]; (1) Interpretacja tej deklaracji: t jest identyfikatorem (nazwą) jednowymiarowej tablicy

danych typu całkowitego o rozmiarze N.

Page 19: Plik zawiera materiał ilustracyjny do wykładuics.p.lodz.pl/~stokfi/aisd/aisd_1.pdf · 3 Literatura uzupełniająca: 1. Drozdek A.: C++ Algorytmy i struktury danych, Helion, 2004

19

Ze względu na to, że możliwe jest definiowanie tablic wielowymiarowych, wprowadza się pojęcia:

wymiaru, który podaje liczbę przestrzennych kierun-ków rozmieszczenia elementów tablicy,

rozmiarów, które określają liczbę elementów tablicy w każdym z wymiarów.

0 1 2 3 4 t

Rys. 10. Przykładowa tablica jednowymiarowa, odpowiadająca deklaracji (1), gdy N=5

Zasadniczą wadą tablicy jest stałość jej rozmiarów. Na ogół

jeżeli chcemy przydzielić w programie pamięć na tablicę, musimy podać konkretną wartość jej rozmiarów. Obecnie próbuje się elastycznie traktować sprawę rozmiarów tablicy. W programowaniu uogólnionym próba wstawienia nowego elementu poza aktualny rozmiar, powoduje automatyczne rozszerzenie zajmowanego przez tablicę obszaru. Jest to jednak operacja kosztowna, związana z przenoszeniem wartości elementów tablicy do nowego, szerszego obszaru pamięci, a następnie zwolnieniem obszaru pamięci dotychczas zajmowanego przez tablicę.

Z punktu widzenia algorytmów, obsługujących tablicę, będziemy ją traktować jako strukturę o stałych rozmiarach. Natomiast niewątpliwą zaletą tablicy jest bezpośredni

dostęp. Dzięki niemu uzyskujemy, poprzez podanie położenia elementu w tablicy (indeksu) np. w zapisie t[i], możliwość natychmiastowego dostępu do tego elementu.

4 -1 7 0 2

indeksy

wartości

Page 20: Plik zawiera materiał ilustracyjny do wykładuics.p.lodz.pl/~stokfi/aisd/aisd_1.pdf · 3 Literatura uzupełniająca: 1. Drozdek A.: C++ Algorytmy i struktury danych, Helion, 2004

20

Obie, wyżej omówione cechy tablicy – stałość rozmiaru i bezpośredni dostęp do jej składowych, wyznaczają dość wąski obszar zastosowań tablicy w programowaniu. Nadaje się ona do przechowywania niewielkich ilości danych, do których chcemy mieć bardzo szybki (bezpośredni) dostęp i których ilość można wcześniej przewidzieć. 6. Typ rekordowy. Unia. Typ rekordowy (zwany czasem typem strukturowym) jest

typem złożonym i podobnie jak typ tablicowy, charakteryzuje go stałość rozmiarów i bezpośredni dostęp. W odróżnieniu jednak od tablicy, składowe danej tego typu (zwane polami), mogą być różnych typów. Dopuszczalne są (jako składowe) wszystkie typy (pierwotny, złożony, lub abstrakcyjny), jeżeli tylko mogą być alokowane w pamięci operacyjnej.

a) Nazwisko Imię Waga Płeć Data_urodz. 25 bajtów 25 bajtów 2 bajty 1 bajt 8 bajtów

b) Nazwisko Imię Waga Płeć Data_urodz. 25 bajtów Rys. 11. Schemat alokacji w pamięci : a) przykładowego rekordu, przechowującego dane osoby, b) te same dane przechowywane w unii

Page 21: Plik zawiera materiał ilustracyjny do wykładuics.p.lodz.pl/~stokfi/aisd/aisd_1.pdf · 3 Literatura uzupełniająca: 1. Drozdek A.: C++ Algorytmy i struktury danych, Helion, 2004

21

Poszczególne pola rekordu (struktury) są alokowane w pamięci jedno za drugim w kolejności zadeklarowania. Odmianą typu rekordowego jest unia. Unia jest zorganizowana w ten sposób, że poszczególne pola są alokowane zawsze w tym samym miejscu pamięci począwszy od bajtu, który jest początkiem alokacji całej unii. W rezultacie rozmiary unii są równe długości największego pola. Unia jest więc doskonałą strukturą do przechowywania danych różnych typów, ale w sytuacji, gdy w danej chwili jest potrzeba przechowywania tylko jednej z nich. W takich sytuacjach unia daje oczywistą oszczędność pamięci.

Rekord jest przykładem struktury danych, w których dostęp

do jego składowych uzyskujemy poprzez nazwę składowej. Niech r będzie nazwą nie dynamicznego rekordu z rys. 11a,

natomiast ref - nazwą wskazania rekordu dynamicznego. Wtedy dostęp do składowej rekordu, przechowującej na przykład wagę osoby, uzyskać można w dwojaki sposób: - dla rekordu nie dynamicznego - za pomocą selektora struk-

turowego w postaci kropki r.waga, - dla rekordu dynamicznego - za pomocą dwuargumentowe-

go operatora selekcji pola rekordu w postaci strzałki refwaga.

Oba zapisy reprezentują ten sam obszar pamięci w rekordzie. Musimy mieć świadomość, że zarówno typ rekordowy, jak i

unia, są w rzeczywistości szczególnymi przypadkami typu obiektowego (klasy, w której nie uwzględnia się funkcji składowych).

Większość omawianych w trakcie tego wykładu typów,

struktur danych i algorytmów ich obsługi została w języku C++ zaimplementowana w Standardowej Bibliotece

Page 22: Plik zawiera materiał ilustracyjny do wykładuics.p.lodz.pl/~stokfi/aisd/aisd_1.pdf · 3 Literatura uzupełniająca: 1. Drozdek A.: C++ Algorytmy i struktury danych, Helion, 2004

22

Szablonów ( STL). Biblioteka ta jest ogromnym zbiorem elementów wielokrotnego użytku w formie ogólnych (szablonowych) klas i funkcji, co jest wielkim ułatwieniem dla programistów. Zwalnia bowiem z konieczności implementowania całego szeregu struktur danych i algorytmów. Jednak komponentów STL można używać prawidłowo tylko wtedy, gdy dobrze rozumie się ich działanie i zastosowania. Właśnie to zrozumienie jest celem tego wykładu.

Koniec części I