Temat: Dynamiczne liniowe struktury danych - stos, kolejka, lista. 1. Wady i zalety struktury tablicy Wady • ograniczony rozmiar maksymalny • konieczno okre lenia stałego rozmiaru tablicy • statyczna alokacja Zalety • prosty indeksowy dost p do elementów tablicy 2. Deklaracja wska nika. Alokacja zmiennych dynamicznych a)Rodzaje pami ci u ywanej w programach Pami komputera, dost pna dla programu, dzieli si na cztery obszary: • kod programu, • dane statyczne ( np. stałe i zmienne globalne programu), • dane automatyczne (zmienne lokalne funkcji - tworzone i usuwane automatycznie przez kompilator) → tzw. STOS (ang. stack) • dane dynamiczne (zmienne, które mo na tworzy i usuwa w dowolnym momencie pracy programu) → w pami ci wolnej komputera → tzw. STERTA (ang. heap) Zmienne dynamiczne → s to zmienne tworzone przez programist w pami ci wolnej komputera (na stercie) → dost p do takiej zmiennej mo liwy jest jedynie poprzez jej adres w pami ci (przechowywany w zmiennej wska nikowej). 1 b) Funkcje przydziału i zwalniania pami ci W j zyku C do dynamicznego przydzielania pami ci (tworzenia zmiennych dynamicznych) słu specjalne funkcje z biblioteki < alloc.h > albo <stdlib.h>. void ∗malloc( size_t rozmiar ); // przydział bloku o zadanej wielko ci void ∗calloc( size_t il_elementow, size_t rozmiar); // przydział // pami ci dla tablicy dynamicznej void free( void ∗wskaznik); // zwolnienie wskazywanego obszaru unsigned coreleft( void ); // sprawdzenie wolnego miejsca na stercie Przykład 1 // przydział i zwolnienie pami ci na stercie int main( ) { int ∗wsk; // zmienna wska nikowa do zapami tania // adresu liczby int int x=5; • • • wsk = (int∗) malloc( sizeof(int) ); // przydzielenie pami ci if( wsk == NULL ) printf( ”Bł d przydziału pami ci” ); else { ∗wsk = 10; // przykładowe operacje na dynamicznej liczbie ∗wsk ∗= 2; printf( ”%d”, ∗wsk ); scanf( ”%d”, wsk ); • • • free( wsk ); // zwolnienie pami ci • • • // dane spod wska nika wsk ju nie s dost pne, a 2 } return 0; } // dane ze zmiennej x s w dalszym ci gu widoczne wsk 20 x Stack (stos) Heap (sterta) Przykład 2 // tablica dynamiczna int rozmiar_tablicy; double ∗T; printf( ”Ile liczb chcesz wprowadzi : ” ); scanf( ”%d”, &rozmiar_tablicy ); if( T = (double∗) calloc( rozmiar_tablicy, sizeof(double) ) ) // przydział pami ci dla tablicy dynamicznej { for( int i = 0; i < rozmiar_tablicy, i++ ); ∗( T+i ) = i*2; // albo T[ i ] =i*2; • • • } free(T); // zwolnienie pami ci przeznaczonej na tablic 3 Przykład 3 // struktura dynamiczna struct Osoba{ char nazwisko[30]; char imie[20]; float zarobki; }; Osoba ∗wsk_osoby; wsk_osoby = (Osoba∗) malloc( sizeof(Osoba) ); // przydział pami ci dla danych typu Osoba, // dane s dost pne przez wska nik wsk_osoby if( wsk_osoby ) // if( wsk_osoby != NULL ) { printf( ”Podaj nazwisko: ” ); gets(wsk_osoby −> nazwisko ); printf( ”Podaj imie: ” ); gets(wsk_osoby −> imie); printf( ”Podaj zarobki: ” ); scanf(„%f”,&(wsk_osoby −> zarobki)); ... wsk_osoby->zarobki+=100.50; ... free( wsk_osoby ); // zwolnienie pami ci przeznaczonej // dla danych typu Osoba } Je eli pod wska nikiem zapami tana jest struktura, to dost p do jej pól jest mo liwy przez u ycie operatora „->” 4 Przykład 4 //dynamiczna tablica struktur int rozmiar_tablicy; Osoba ∗T; printf( ”Ile liczb chcesz wprowadzi : ” ); scanf( ”%d”, &rozmiar_tablicy ); T = (Osoba∗) calloc( rozmiar_tablicy, sizeof(Osoba)); // przydział pami ci dla tablicy dynamicznej if( T == NULL ) printf( ”Bł d przydziału pami ci” ); else for( int i = 0; i < rozmiar_tablicy, i++ ); { printf( ”Podaj nazwisko: ” ); gets(( T+i ) −> nazwisko ); // albo gets(T[i].nazwisko); printf( ”Podaj imie: ” ); gets((T+i)->imie); // albo gets(T[i].imie); • • • printf(„Podaj zarobki:”); scanf(„%f”,&((T+i)->zarobki)); // albo scanf(„%f”,&T[i].zarobki); } • • • free( T ); // zwolnienie pami ci przeznaczonej dla tablicy // dynamiczne 5 3. Stos Stos to struktura umo liwiaj ca wstawianie i usuwanie danych dost pnych jedynie w tzw. wierzchołku stosu. W wierzchołku stosu znajduj si zawsze dane, które zostały wstawione do stosu w trakcie ostatnio wykonanej operacji wstawiania. a) Deklaracja typu definiuj cego element struktury stosu struct Element{ typ_danych dane; // typ danych przechowywanych w stosie Element *nastepny; // adres na nast pny element stosu }; W dalszej cz ci wykładu b dziemy dla uproszczenia zakładali, e przechowujemy w strukturze liczby całkowite (typ_danych=int) b) Operacja push wstawiania do stosu Element *push(Element *S, int d) Przed wywołaniem push(S, d) S 16 dane nastepny Po wywołaniu push(S, d) S d 20 16 8 20 NULL 8 NULL 6 Element *push(Element *S, int d) { Element *temp; temp=S; S=(Element *)malloc(sizeof(Element)); S->dane=d; S->nastepny=temp; return S; } c) Operacja pop usuwania ze stosu Element *pop(Element *S) Przed wywołaniem pop(S) 16 S dane nastepny Po wywołaniu pop(S) 20 S 20 8 8 NULL NULL 7 Element *pop(Element *S) { Element *temp; if (S!=NULL) { temp=S->nastepny; free(S); S=temp; } return S; } d) Operacja top zwracaj ca dane przechowywane w wierzchołku stosu int top(Element *S) { return S->dane; } Uwaga !!! Operacja top powinna by wywołana tylko wtedy, gdy stos jest niepusty e) Operacja empty sprawdzaj ca, czy stos jest niepusty int empty(Element *S) { if (S==NULL) return 1; else return 0; } 8 Przykład 5 // drukowanie liczb ze stosu z jednoczesnym jego kasowaniem Element *drukuj_i_usun(Element *S) { while (!empty(S)) { printf(„%d\n”, top(S)); S=pop(S); } return S; } 4. Kolejka Kolejka, to struktura w której mo na wstawi element za tym, który trafił do struktury jako ostatni, a usuwa ten, który został wstawiony jako pierwszy. a) Deklaracja typu definiuj cego element struktury kolejki struct Element{ typ_danych dane; // typ danych przechowywanych w stosie Element *nastepny; // adres na nast pny element stosu }; struct Kolejka { Element *first, *last; } W dalszej cz ci wykładu b dziemy dla uproszczenia zakładali, e przechowujemy w strukturze liczby całkowite (typ_danych=int) 9 b) Operacja in wstawiania do kolejki Kolejka in(Kolejka Q, int d ) Przed wywołaniem in(Q, d) 16 first Q Po wywołaniu in(Q, d) dane nastepny 16 first 20 20 Q last 8 last NULL 8 d NULL Kolejka in(Kolejka Q, int d) { Element *temp; temp=Q.last; Q.last=(Element *)malloc(sizeof(Element)); Q.last->dane=d; Q.last->nastepny=NULL; if (temp!=NULL) temp->nastepny=Q.last; else Q.first=Q.last; return Q; } 10 c) Operacja out usuwania z kolejki Kolejka out(Kolejka Q) Przed wywołaniem out(Q) 16 Q first 20 Po wywołaniu out(Q) dane nastepny 20 first Q 8 last last 8 NULL NULL Kolejka out(Kolejka Q) { Element *temp; if (Q.first!=NULL) { temp = Q.first->nastepny; free(Q.first); Q.first = temp; if (Q.first==NULL) Q.last=NULL; } return Q; } 11 d)Operacja first zwracaj ca dane przechowywane w pierwszym elemencie kolejki int first(Kolejka Q) { return Q.first->dane; } Uwaga !!! Operacja first powinna by wywołana tylko wtedy, gdy kolejka jest niepusta e)Operacja empty sprawdzaj ca, czy kolejka jest niepusta int empty(Kolejka Q) { if (Q.first==NULL) return 1; else return 0; } Przykład 6 // wyznaczanie sumy liczb zapami tanych w kolejce z jednoczesnym // kasowaniem kolejki long sumuj_i_usun(Kolejka Q) { long suma=0; while (!empty(Q)) { suma+= first(Q); Q=out(Q); } return suma; } 12 5. Lista Lista jest struktur , w której operacja wstawiania i usuwania nie ma ci le ustalonego miejsca, tzn. mo emy wstawia (usuwa ) element z pocz tku, ko ca lub rodka listy. a) Deklaracja typu definiuj cego element struktury kolejki struct Element{ typ_danych dane; // typ danych przechowywanych w li cie Element *nastepny; // adres na nast pny element listy }; W dalszej cz ci wykładu b dziemy dla uproszczenia zakładali, e przechowujemy w strukturze liczby całkowite (typ_danych=int) a) Operacja add wstawiania do listy Element *add(Element *first, int d, Element *current) Przed add(first, d, current) F 16 current 20 8 NULL dane nastepny Po add(first, d, current) F 16 20 current d 8 NULL 13 Gdy parametr current=NULL, to nowy element jest wstawiany jako pierwszy na li cie (pod adres first). Element *add(Element *F, int d, Element **current) { Element *temp; if (*current == NULL) { temp=F; F=(Element *)malloc(sizeof(Element)); F->dane=d; F->nastepny=temp; *current=F; } else { temp=*current; *current=(Element *)malloc(sizeof(Element)); (*current)->dane=d; (*current)->nastepny=temp->nastepny; temp->nastepny=*current; } return F; } 14 c)Operacja delete usuwania z Listy Element *delete (Element *F, int *d, Element **current) Przed delete(F, d, current) F 16 current 20 dane nastepny Po wywołaniu delete(F, d, current) F current 16 8 d = 20 8 NULL NULL Gdy parametr current == NULL, to wywołanie operacji delete nie zmienia listy. Element *delete(Element *F, int *d, Element **current) { Element *temp; if (F!=NULL && *current!=NULL) if (*current == F) { temp=F->nastepny; *d=F->dane; free(F); F=temp; *current=F; } else { temp=F; while (temp!=NULL && *current!=temp->nastepny) { 15 } } return F; temp=temp->nastepny; if (temp!=NULL) { temp->nastepny=(*current)->nastepny; *d=(*current)->dane; free(*current); *current=temp->nastepny; } } c)Funkcja get_address zwracaj ca adres danej warto ci Funkcja zwraca adres pusty, gdy szukanego elementu nie ma na li cie albo adres podanej warto ci danych, o ile ta warto wyst puje na li cie. Element *get_address(Element *F, int d) { Element *temp; temp=F; while (temp!=NULL && temp->dane!=d) temp=temp->nastepny; return temp; } 16 Przykład 7 // wstawianie na list liczb od 0 do 8 // sprawdzenie, czy na li cie wyst puje liczba 9 // drukowanie elementów listy poł czone z ich usuwaniem Element *F=NULL,*current=NULL; int d; F=add(F,0,&current); current=F; for (int i=1;i<9;i++) F=add(F,i,&current); Element *tmp=get_address(F,9); if (tmp!=NULL) printf("%d\n",tmp->dane); else printf("NULL\n"); current=F; while (F!=NULL) { F=delete(F,&d,&current); printf("%d\n",d); } 17