Dynamiczne struktury danych 391 Dynamiczne struktury danych Przez dynamiczne struktury danych rozumiemy proste i złożone struktury danych, którym pamięć jest przydzielana i zwalniana na żądanie w trakcie wykonywania programu. Głównymi elementami konstrukcyjnymi struktur dynamicznych są zmienne dynamiczne oraz wskaźniki. Główną zaletą struktur dynamicznych jest ich duża elastyczność, o której decyduje możliwość dynamicznego tworzenia i usuwania poszczególnych elementów oraz możliwość operowania ich adresami. 392 dr inż. P. Borowiecki (KAMS, WETI, PG) 1
Dynamiczne struktury danych Wybrane dynamiczne struktury danych Elementarne tworzone dynamicznie zmienne typów prostych, dynamicznie tworzone struktury (rekordy), tworzone dynamicznie obiekty różnych klas. Podstawowe dynamiczne tablice jedno i wielowymiarowe, listy liniowe jednokierunkowe, listy liniowe dwukierunkowe, listy cykliczne jednokierunkowe, listy cykliczne dwukierunkowe. Zaawansowane listy łączone, listy inwersyjne (listy odsyłaczy, listy wskaźników), listy list, struktury grafowe (listy sąsiedztw, drzewa, drzewa binarne, drzewa czwórkowe,...). 393 Definiowanie listy 3 5 23 12 struct element int info; element *nast; ; element *; info nast 394 dr inż. P. Borowiecki (KAMS, WETI, PG) 2
Przeglądanie listy 3 5 23 12 // Przeglądanie i wypisanie wartosci elem. listy. // Gdy lista jest pusta wyswietlany jest komunikat. void PiszListe(element *) if (!= ) do cout << setw(6) << (->info) << endl; = ->nast; while(!= ); else cout << "Lista pusta" << endl; 395 Tworzenie listy // Funkcja tworzy listę odwroconą. // Na atku listy znajduje sie element podany jako ostatni. // Parametrem jest referencja do wskaźnika // ( pozwala to na modyfikację wartości parametru aktualnego // będącego wskaźnikiem na ątek listy ) void TworzListeOdwr(element *&) int wartosc; char odp; cout << "Podaj pierwszy element listy" << endl; do cout<< ": "; cin >> wartosc; DolaczPocz(,wartosc); cout << "Czy nastepny element? (T/N)" << endl; cin >> odp; while(toupper(odp)!= 'N'); 396 dr inż. P. Borowiecki (KAMS, WETI, PG) 3
Tworzenie listy c.d. // Tworzy nowy element listy oraz aktualizuje wskaźniki // odpowiedzialne za jej strukturę void DolaczPocz (element *&, int wartosc) nowy->info = wartosc; nowy->nast = ; = nowy;?? 5 23 12 Stan listy po wykonaniu instrukcji alokacji pamięci dla nowego elementu nowy 397 Tworzenie listy c.d. void DolaczPocz (element *&, int wartosc) nowy->info = wartosc; // równoważnie (*nowy).info nowy->nast = ; = nowy; 3 5 23 12 nowy Stan listy po wykonaniu instrukcji ustalających wartości pól nowego elementu nowy->info = wartosc; nowy->nast = ; 398 dr inż. P. Borowiecki (KAMS, WETI, PG) 4
Tworzenie listy c.d. void DolaczPocz (element *&, int wartosc) nowy->info = wartosc; nowy->nast = ; = nowy; 3 5 23 12 nowy Stan listy po wykonaniu instrukcji ustalającej nową wartość wskaźnika ątku listy = nowy; 399 Tworzenie listy c.d. Skąd bierze się na końcu listy? Zauważ, że utworzenie listy jednoelementowej polega na dołączeniu pierwszego elementu do listy pustej reprezentowanej przez wskaźnik o wartości.?? 12 12 nowy nowy nowy Pamiętaj aby pustą listę zawsze reprezentować przez wskaźnik o wartości!! 400 dr inż. P. Borowiecki (KAMS, WETI, PG) 5
Tworzenie listy nieodwróconej // Funkcja tworzy listę nieodwroconą // Na atku listy znajduje sie element podany jako pierwszy. // Najpierw DolaczPocz tworzy pierwszy element listy, // potem funkcja WstawPo wywolywana ze wskazaniem // na ostatni element dodaje elementy na koncu listy. void TworzListe (element *&) char odp; element *ost; int wartosc; cout << "Podaj pierwszy element listy"; cout << " : "; cin >> wartosc; DolaczPocz(,wartosc); ost = ; cout << "Czy nastepny element? (T/N)> "; cin >> odp; while (toupper(odp) == 'N') cout << ": "; cin >> wartosc; ost = WstawPo(ost,wartosc); cout << "Czy nastepny element? (T/N)> "; cin >> odp; 401 Tworzenie listy nieodwróconej c.d. // Funkcja wstawiajaca nowy element za elementem wskazywanym przez wsk. // Zwraca wskaznik na dodany element. // Wartosc parametru wsk na wejsciu musi byc rozna od (niepusta lista). // Wskaznik wsk moze byc wskaznikiem na dowolny element listy. element* WstawPo (element *wsk, int wartosc) nowy->info = wartosc; nowy->nast = wsk->nast; wsk->nast = nowy; return nowy; 402 dr inż. P. Borowiecki (KAMS, WETI, PG) 6
Wstawianie elementu PRZED elementem wskazywanym // Funkcja wstawiajaca nowy element przed elementem wskazywanym // przez wsk. // Wartosc parametru wsk na wejsciu musi byc rozna od. // Wskaznik wsk moze byc wskaznikiem na dowolny element listy. void WstawPrzed (element *wsk, int wartosc) *nowy = *wsk; wsk->info = wartosc; wsk->nast = nowy; 403 Szukanie na liście określonego elementu // Poszukuje na liście elementu o wartości szukany w polu info. // Jako wynik zwraca wskaznik do znalezionego elementu // albo, gdy na liście nie ma odpowiedniego elementu. element* Szukaj(element *wsk, int szukany) while ((wsk!= ) and (wsk->info!= szukany)) wsk = wsk->nast; if (wsk == ) return ; else return wsk; 404 dr inż. P. Borowiecki (KAMS, WETI, PG) 7
// Kasowanie elementu wskazywanego przez wsk // Brak dostepu do elementu poprzedzajacego rozwiazany podobnie // jak dla WstawPrzed przez kopiowanie zawartosci nastepnego elementu // Wymagane jest jednak aby nastepny element istnial, dlatego ta metoda // nie mozna usunac ostatniego elementu listy (w szczegolnosci pierwszego // elementu listy jednoelementowej). Poczatek listy pozostaje bez zmian. void Kasuj (element* wsk) element *pom; Kasowanie wskazywanego elementu listy if (wsk->nast!= ) // jezeli istnieje nastepny element pom = wsk->nast; *wsk = *pom; delete pom; 405 Kasowanie elementu ZA elementem wskazywanym // Kasowanie elementu nastepujacego po elemencie wskazywanym przez poprz. // Lista musi miec przynajmniej jeden element. // Usunac mozna kazdy element pod warunkiem, ze parametr wejsciowy // poprz bedzie wskazywal na jego poprzednik. // Przyjeto, ze jezeli poprz== to usuwany jest pierwszy element. void KasujZa (element *&, element* poprz) element* pom; if (poprz == ) // usuwany jest pierwszy element listy pom = ; = ->nast; delete pom; else pom = poprz->nast; poprz->nast = pom->nast; delete pom; 406 dr inż. P. Borowiecki (KAMS, WETI, PG) 8
Zwalnianie pamięci zajmowanej przez listę // Zwalania pamiec zajmowana przez kolejne elementy listy. // Po zakonczeniu wartosc rowna jest void ZwolnijListe(element * &) element *temp; while (!= ) temp = ; = ->nast; delete temp; 407 Lista liniowa jednokierunkowa (zastosowania) Implementacja stosu (ang. stack) Podstawowe operacje: 1. dodanie elementu na szczycie stosu (PUSH) 2. pobranie elementu ze szczytu stosu (POP) 3 5 23 12 LIFO (ang. last in,first out) top Kierunek wskazań wybrano tak, aby operacje PUSH i POP były realizowane w stałym czasie. Zauważ, że odwrócenie wskazań spowodowałoby znaczne pogorszenie efektywności operacji POP. 408 dr inż. P. Borowiecki (KAMS, WETI, PG) 9
Lista liniowa jednokierunkowa (zastosowania) Implementacja kolejki (ang. queue) Podstawowe operacje: 1. dodanie elementu na końcu kolejki (ENQUEUE) 2. pobranie elementu z ątku kolejki (DEQUEUE) 3 5 23 12 head FIFO (ang. first in, first out) tail Kierunek wskazań wybrano tak, aby operacje ENQUEUE i DEQUEUE były realizowane w stałym czasie. Zauważ, że odwrócenie wskazań spowodowałoby znaczne pogorszenie efektywności operacji DEQUEUE. 409 Lista jednokierunkowa cykliczna Definiowanie listy 3 5 23 12 struct element int info; element *nast; ; element *; info nast 410 dr inż. P. Borowiecki (KAMS, WETI, PG) 10
Implementacja listy liniowej dwukierunkowej Definiowanie listy 35 102 17 lewy struct element element *poprz; int info; element *nast; ; element *lewy; element *prawy; prawy poprz info nast 411 Implementacja listy liniowej dwukierunkowej Dodanie elementu na lewym końcu listy 35 102 17 lewy prawy void DolaczLewy (element *&lewy, int wartosc) nowy->info = wartosc; nowy->nast = lewy; nowy->poprz = ; lewy->poprz = nowy; lewy = nowy; 412 dr inż. P. Borowiecki (KAMS, WETI, PG) 11
Implementacja listy liniowej dwukierunkowej Kasowanie elementu wewnętrznego 35 102 17 lewy wsk prawy Stan listy po wykonaniu dwóch pierwszych instrukcji void KasujWew (element* wsk) wsk->poprz->nast = wsk->nast; wsk->nast->poprz = wsk->poprz; delete wsk; 413 Implementacja listy liniowej dwukierunkowej I dalej analogicznie jak dla listy jednokierunkowej. < patrz również lista zadań > 414 dr inż. P. Borowiecki (KAMS, WETI, PG) 12
Definiowanie listy Lista dwukierunkowa cykliczna 35 102 17 struct element element *poprz; int info; element *nast; ; element *; poprz info nast 415 dr inż. P. Borowiecki (KAMS, WETI, PG) 13