Rok akademicki 2012/2013, Wykład nr 2 2/25 Plan wykładu nr 2 Informatyka 2 Politechnika Białostocka - Wydział Elektryczny Elektrotechnika, semestr III, studia niestacjonarne I stopnia Rok akademicki 2012/2013 Dynamiczny przydział pamięci w języku C przydział pamięci na macierz Dynamiczne struktury danych stos, kolejka, lista, drzewo Wykład nr 2 (19.10.2012) dr inż. Jarosław Forenc Rok akademicki 2012/2013, Wykład nr 2 3/25 Rok akademicki 2012/2013, Wykład nr 2 4/25 Dynamiczny przydział pamięci na macierz (1) Dynamiczny przydział pamięci na macierz (1) przydzielamy dynamicznie pamięć na macierz zawierającą N-wierszy i M-kolumn Metoda 1 (wektor N M-elementowy) przydzielamy pamięć na wektor N M-elementowy N [0][0] [0][1] [0][2] [0][3] [1][0] [1][1] [1][2] [1][3] [2][0] [2][1] [2][2] [2][3] po wykorzystaniu wektora, przydzieloną pamięć zwalniamy funkcją free() M zamiast standardowego odwołania do elementów macierzy: tab[i][j] stosujemy odwołania do odpowiednich elementów wektora: *(tab+i*m+j) lub tab[i*m+j] Przykład: tablica tab, dla której N=3, M=4 odwołujemy się do elementu tab[2][2]: *(tab+2*4+2) int *tab; tab = (int*) calloc(n*m,sizeof(int)); /*... */ free(tab);
Rok akademicki 2012/2013, Wykład nr 2 5/25 Rok akademicki 2012/2013, Wykład nr 2 6/25 Dynamiczny przydział pamięci na macierz (1) Dynamiczny przydział pamięci na macierz (2) #include <stdio.h> #include <stdlib.h> #include <time.h> #define N 4 #define M 6 int main() int i,j,*tab; } tab = (int *) calloc(n*m,sizeof(int)); srand(time(null)); for (i=0;i<n;i++) for (j=0;j<m;j++) *(tab+m*i+j) = rand()%100; for (i=0;i<n;i++) for (j=0;j<m;j++) printf("%4d",tab[m*i+j]); printf("\n"); } free(tab); return 0; 77 59 49 41 82 62 89 80 86 52 7 64 51 43 4 29 23 98 52 10 2 43 37 95 Metoda 2 (wskaźnik na tablicę wskaźników) przydzielamy pamięć na N-elementowy wektor wskaźników do typu int int ** int * odwołania do elementów takiej tablicy mają taką samą postać jak w przypadku zwykłych tablic: tab[i][j] przydzielona pamięć nie stanowi ciągłego obszaru w pamięci komputera int Rok akademicki 2012/2013, Wykład nr 2 7/25 Rok akademicki 2012/2013, Wykład nr 2 8/25 Dynamiczny przydział pamięci na macierz (3) Dynamiczne struktury danych Metoda 3 (wektor N M-elementowy + wektor wskaźników) przydzielamy pamięć na N-elementowy wektor wskaźników do typu int i na N M-elementowy wektor elementów typu int, którego adres zapisujemy pod zerowym elementem wektora N-elementowego int ** int * do pozostałych elementów wektora N-elementowego zapisujemy adresy pierwszych elementów kolejnych wierszy macierzy znajdujących się w wektorze N M-elementowym odwołania do elementów: tab[i][j] int Dynamiczne struktury danych - struktury danych, którym pamięć jest przydzielana i zwalniana w trakcie wykonywania programu stos, kolejka lista (jednokierunkowa, dwukierunkowa, cykliczna) drzewo Elementy w dynamicznych strukturach danych są strukturami składającymi się z użytecznych danych (data) oraz z jednego lub kilku wskaźników (next) zawierających adresy innych elementów struct element typ data; struct element *next;
Rok akademicki 2012/2013, Wykład nr 2 9/25 Rok akademicki 2012/2013, Wykład nr 2 10/25 Stos Stos - zastosowanie: notacja polska stos (ang. stack) - struktur składająca się z elementów, z których każdy posiada tylko adres następnika dostęp do danych przechowywanych na stosie jest możliwy tylko w miejscu określanym mianem wierzchołka stosu (ang. top) tutaj dodajemy i usuwamy elementy top notacja polska (zapis przedrostkowy, Notacja Łukasiewicza) jest to sposób zapisu wyrażeń arytmetycznych, podający najpierw operator, a następnie argumenty wyrażenie arytmetyczne: wierzchołek stosu jest jedynym miejscem, do którego można dołączać lub z którego można usuwać elementy każdy składnik stosu posiada wyróżniony element (next) zawierający adres następnego elementu wskaźnik ostatniego elementu stosu wskazuje na adres pusty (NULL) podstawowe operacje na stosie to: dodanie elementu do stosu - funkcja push() zdjęcie elementu ze stosu - funkcja pop() data next data next data next NULL wierzchołek 4 / (1 + 3) ma w notacji polskiej postać: / 4 + 1 3 wyrażenie w notacji polskiej nie wymaga nawiasów, ponieważ przypisanie argumentów do operatorów wynika wprost z ich kolejności w zapisie notacja polska była podstawą opracowania tzw. odwrotnej notacji polskiej Rok akademicki 2012/2013, Wykład nr 2 11/25 Rok akademicki 2012/2013, Wykład nr 2 12/25 Stos - zastosowanie: odwrotna notacja polska Stos - zastosowanie: odwrotna notacja polska odwrotna notacja polska - ONP (ang. Reverse Polish Notation, RPN) jest sposobem zapisu wyrażeń arytmetycznych, w którym operator umieszczany jest po argumentach wyrażenie arytmetyczne: (1 + 3) / 2 ma w odwrotnej notacji polskiej postać: 1 3 + 2 / Obliczenie wartości wyrażenia przy zastosowaniu ONP wymaga: zamiany notacji konwencjonalnej (nawiasowej) na ONP (algorytm Dijkstry nazywany stacją rozrządową) obliczenia wartości wyrażenia arytmetycznego zapisanego w ONP W obu powyższych algorytmach wykorzystywany jest stos Przykład: wyrażenie arytmetyczne: (2 + 1) * 3 4 * (7 + 4) odwrotna notacja polska została opracowana przez australijskiego naukowca Charlesa Hamblina ma w odwrotnej notacji polskiej postać: 2 1 + 3 * 4 7 4 + * -
Rok akademicki 2012/2013, Wykład nr 2 13/25 Rok akademicki 2012/2013, Wykład nr 2 14/25 Kolejka Lista jednokierunkowa Kolejka - składa się z liniowo uporządkowanych elementów Elementy dołączane są tylko na końcu kolejki (wskaźnik tail) Elementy usuwane są tylko z początku kolejki (wskaźnik head) Organizacja listy jednokierunkowej podobna jest do organizacji stosu i kolejki Dla każdego składnika (poza ostatnim) jest określony następny składnik (lub poprzedni - zależnie od implementacji) Powiązanie między elementami kolejki jest takie samo, jak w stosie Kolejka nazywana jest stosem FIFO (ang. First In First Out) Zapamiętywany jest wskaźnik tylko na pierwszy element listy (first) lub wskaźniki na pierwszy (first) i ostatni element listy (last) Elementy listy można dołączać/usuwać w dowolnym miejscu listy Rok akademicki 2012/2013, Wykład nr 2 15/25 Rok akademicki 2012/2013, Wykład nr 2 16/25 Lista dwukierunkowa Lista cykliczna Każdy węzeł posiada adres następnika, jak i poprzednika W strukturze tego typu wygodne jest przechodzenie pomiędzy elementami w obu kierunkach (od początku do końca i odwrotnie) Powstaje z listy jednokierunkowej lub dwukierunkowej, poprzez połączenie ostatniego element z pierwszym Jednokierunkowa: Dwukierunkowa:
Rok akademicki 2012/2013, Wykład nr 2 17/25 Rok akademicki 2012/2013, Wykład nr 2 18/25 Drzewo Drzewo binarne Najbardziej ogólna dynamiczna struktura danych, może być reprezentowane graficznie na różne sposoby Szczególny przypadek ogólnej struktury zwanej drzewem Na górze znajduje się korzeń drzewa (a) Skojarzone z korzeniem poddrzewa połączone są z nim liniami zwanymi gałęziami drzewa Każdy wierzchołek drzewa binarnego ma co najwyżej dwóch potomków Potomkiem węzła w nazywamy każdy, różny od w, węzeł należący do drzewa, w którym w jest korzeniem Węzeł, który nie ma potomków, to liść drzewa Rok akademicki 2012/2013, Wykład nr 2 19/25 Rok akademicki 2012/2013, Wykład nr 2 20/25 Binarne drzewo wyszukiwawcze Drzewo binarne, w którym dla każdego węzła w i : wszystkie klucze w lewym poddrzewie węzła w i są mniejsze od klucza w węźle w i wszystkie klucze w prawym poddrzewie węzła w i są większe od klucza w węźle w i Struktura - zestaw elementów różnych typów, zgrupowanych pod jedną nazwą Deklaracja struktury rozpoczyna się od słowa kluczowego struct, po którym zazwyczaj występuje nazwa struktury (etykieta) Pomiędzy nawiasami klamrowymi umieszczone są pola struktury (komponenty, składowe), mające taką samą postać jak deklaracje zmiennych w programie struct nazwa opis_pola_1; opis_pola_2;... opis pola_n; Zaleta: szybkość wyszukiwania informacji W deklaracji struktury muszą występować nawiasy klamrowe i średniki
Rok akademicki 2012/2013, Wykład nr 2 21/25 Rok akademicki 2012/2013, Wykład nr 2 22/25 Pola jednego typu można łączyć przecinkami Nazwy pól struktury mogą być takie same jak nazwy innych zmiennych w programie, a nawet takie same jak nazwa struktury Deklarując strukturę wprowadzamy nowy typ danych (np. struct punkt), którym można posługiwać się tak samo jak każdym innym typem standardowym struct punkt int x; int y; struct czas int h, m, s; char imie[20]; char nazwisko[30]; int wiek, waga; Po klamrze kończącej listę pól struktury może występować deklaracja zmiennych strukturalnych Kowal i Nowak są zmiennymi typu W przypadku deklaracji, po której nie występuje lista zmiennych, nie następuje przydział pamięci Jeśli podana została nazwa struktury, to zmienne można zadeklarować później char imie[20]; char nazwisko[30]; int wiek; } Kowal, Nowak; char imie[20]; char nazwisko[30]; int wiek; int main() Kowal; Nowak;... Rok akademicki 2012/2013, Wykład nr 2 23/25 Rok akademicki 2012/2013, Wykład nr 2 24/25 Dostęp do pól struktury możliwy jest dzięki konstrukcji typu: nazwa_struktury.nazwa_pola Gdy zmienna strukturalna jest wskaźnikiem, to do odwołania do pola struktury używamy operatora pośredniego wyboru pola (->) Operator. nazywany jest operatorem bezpośredniego wyboru pola Nowak, *Nowak1; Nowak1 = &Nowak Nowak1 -> wiek = 25; Zapisanie wartości 25 do pola wiek zmiennej Nowak ma postać /* lub */ (*Nowak1).wiek = 25; Nowak.wiek = 25; Zapisanie wartości Jan do pola imie zmiennej Nowak ma postać W ostatnim zapisie nawiasy są konieczne, gdyż operator. ma wyższy priorytet niż operator * strcpy(nowak.wiek,"jan");
Rok akademicki 2012/2013, Wykład nr 2 25/25 Koniec wykładu nr 2 Dziękuję za uwagę!