530 likes | 967 Views
Dynamiczne struktury danych Listy, Kolejki, Stosy. Lista jednokierunkowa. Lista dwukierunkowa. Lista cykliczna jednokierunkowa. Lista cykliczna dwukierunkowa. Lista z przeskokami. z równo rozmieszczonymi węzłami. z nierówno rozmieszczonymi węzłami. Listy samoorganizujące się.
E N D
Lista z przeskokami z równo rozmieszczonymi węzłami z nierówno rozmieszczonymi węzłami
Listy samoorganizujące się Najważniejsze sposoby organizowania list: • Metoda przesuwania na początek • Metoda transpozycji • Metoda zliczania • Metoda porządkowania.
Operacja na listach • Zakładamy, że operujemy na listach nieposortowanych i dwukierunkowych.
Przeszukiwanie listy void lista_szukaj(lista,k){ x = lista.glowa while (x!=NIL)&&(x.klucz!=k) x=x.nastepny return x; }
Wstawianie elementów void lista_wstaw(lista, x){ x.nastepny = L.glowa if L.glowa!=NIL L.glowa.poprzedni =x L.glowa=x x.poprzedni = NIL }
Usuwanie elementów voidlista_usun(lista, x){ jeśli x.poprzedni !=NIL x.poprzedni.nastepny=x.nastepny else lista.glowa=x.nastepny ifx.nastepny!=NIL x.nastepny.poprzedni=x.poprzedni }
Wartownicy • Wartownik to element, który wstawia się do listy by uprościć warunki brzegowe. • Dla omawianej listy przyjmijmy że będzie nim element Lista.nil, wówczas każde występienie stałej NIL w naszych procedurach zmieniamy na wskaźnik do wartownika Lista.nil. • Nasza lista dwukierunkowa staje się listą cykliczną, w której wartownik Lista.nil znajduje się między głową a ogonem czyli atrybut Lista.nil.nastepny wskazuje na glowe listy, a Lista.nil.poprzedni wskazuje na ogon listy. Zarówno atrybut nastepny ogona i atarybut poprzedni glowy listy wskazuja na lista.nil.
Wartownicy - modyfikacja voidlista_szukaj_w(lista,k){ x = lista.nil.nastepny while (x!=lista.nil)&&(x.klucz!=k) x=x.nastepny return x; } voidlista_wstaw_w(lista, x){ x.nastepny = lista.nil.nastepny lista.nil.nastepny.poprzedni=x lista.nil.nastepny=x x.poprzedni=lista.nil } voidlista_usun_w(lista, x){ x.poprzedni.nastepny=x.nastepny x.nastepny.poprzedni=x.poprzedni }
Lista – jako tablica • Wielowymiarowa tablica • Jednowymiarowa tablica
Kolejka Struktura, w której dostęp do danych możliwy jest z dwóch końców, z tym, że z jednego końca dane są usuwane a do drugiego dodawane. Kolejka to struktura FIFO (First in/ First out) – pierwszy wchodzi, pierwszy wychodzi.
Operacje - pseudokod • Dokładanie elementu voidenqueue(int kolejka[], int liczba){ kolejka[ogon]=liczba; if (ogon==n ) // n – rozmiar tablicy kolejka; ogon=1; else ogon++; } • Usuwanie elementu kolejki intdequeue (int kolejka[]){ liczba=kolejka[glowa]; ifglowa==n glowa=1; elseglowa++; return liczba; }
Stos Liniowa struktura danych, do której dostęp możliwy jest z jednego końca. Nazywamy czasem strukturą LIFO (Last in/first out) – ostatni wchodzi a pierwszy wychodzi.
Operacje - pseudokod • Sprawdzanie czy stos jest pusty intstospusty(int stos[]){ if stos[wierzcholek]==0 return 1; else return 0; } Dołóż na stos void push(intstos[], intliczba){ stos[wierzcholek]++; stos[stos[wierzcholek]]=liczba; }
Operacje - pseudokod • Zdejmij ze stosu int pop(int stos[]){ ifstospusty(stos) { cout<<”pusty stos”; return -1; }else{ stos[wierzcholek]--; return Stos[Stos[wierzcholek]+1]; } }
Drzewa ukorzenione • Struktura, składająca się z węzłów, gdzie każdy węzeł zawiera klucz i wskaźnik do innych węzłów
Drzewo binarne Każdy węzeł x ma trzy atrybuty: p – górne pole, left – lewe dolne pole i right – prawe dolne pole. Atrybut klucz nie został uwzględniony na tym rysunku.
Drzewa o dowolnym stopniu rozgałęzień – sposoby prezentacji • Liczba synów każdego węzła jest nie większa niż pewna stała. • Podejście „na lewo syn, na prawo brat”. Każdy węzeł drzewa ma atrybut p wskazujący na ojca (T. root wskazuje na korzeń drzewa T), oraz dwa wskaźniki: • x.left-child – wskazuje na najbardziej lewego syna węzła x. • x.right-sibling wskazuje na najbliższego, znajdującego się na prawo brata węzła x.
Drzewa wyszukiwań binarnych BST – binarysearchtree. • Struktura - drzewem binarnym. • Każdy węzeł zawiera atrybuty: key i ewentualnie left, right, oraz p, które wskazują odpowiednio na lewego, prawego syna oraz ojca. • Klucze spełniają własnośćniech x będzie węzłem drzewa BST i ma dwóch potomków y – lewy syn i z – prawy syn, wtedy zachodzi własność, y.key≤x.key oraz z.key≥x.key.
BST – wypisanie elementów • Metoda inorder- klucz korzenia wypisuje się pomiędzy wartościami z jego lewego poddrzewa oraz prawego podrzewa. • metoda preorder - klucz korzenia wypisanyj jest przed wypisaniem wartości znajdujących się w obu poddrzewach. • metoda postorder - klucz korzenia po wypisaniu wartości znajdującech się w poddrzewach.
BST - Inorder INORDER-TREE-WALK(x) if x!= NIL INORDER-TREE-WALK(x.left) wypiszx.key INORDER-TREE-WALK(x.right) Twierdzenie • Jeśli x jest korzeniem poddrzewa o n węzłach, to wykonanie INORDER-TREE-WALK(x) odbywa się w czasie (n).
BST – wyszukiwanie - rekurencyjnie TREE-SEARCH(x,k) if x==NIL lub k==x.key return x if k<x.key return TREE-SEARCH(x.left,k) else return TREE-SEARCH(x.right, k)
BST – wyszukiwanie ITERATIVE-TREE-SEARCH(x,k) while x!=NIL i k!=x.key if k<x.key x=x.left else x=x.right return x
BST – wyszukiwanie - przykład Szukamy węzła o wartości klucza 9. TREE-SEARCH(15,9) 9<15 TREE-SEARCH(6,9) 9>6 TREE-SEARCH(9,9) 9=9 return 9
BST – Minimum, Maksimum TREE-MINIMUM(x) while x.left!=NIL x=x.left return x TREE-MAXIMUM(x) while x.right!=NIL x=x.right return x Koszt = O(h), gdzie h to wysokość drzewa.
BST – Następnik, Poprzednik TREE-SUCCESSOR(x) ifx.right != NIL return TREE-MINIMUM(x.right) y=x.p while y!=NIL i x==y.right x=y y=y.p return y
BST - wstawianie TREE-INSERT(T,z) y=NIL, x=T.root while x!=NIL y=x ifz.key<x.key x=x.left else x=x.right x.p=y if y==NIL T.root =z Elseifz.key<y.key y.left=z elsey.right=z
BST - Usuwanie • Jeśli z nie ma lewego syna to zastępujemy z przez jego prawego syna (być może równego NIL). Jeśli prawy syn jest równy NIL to z jest liściem a jeśli jest różny od NIL to z ma tylko jednego prawego syna. • Jeśli z ma tylko jednego lewego syna, to zastępujemy z przez jego lewego syna. • W przeciwnym razie z ma zarówno lewego, jak i prawego syna. Znajdujemy węzeł y będący następnikiem z w prawym poddrzewie z. Węzeł y nie ma lewego syna. Chcemy wysłuskać y z jego aktualnego położenia i zastąpić nim węzeł z w drzewie. • Jeśli y jest prawym synem z, to zastępujemy z przez y, pozostawiając prawego syna y bez zmian. • W przeciwnym razie y znajduje się w prawym poddrzewie z, ale nie jest prawym synem z. W tym przypadku najpierw zastępujmy y przez jego prawego syna, a potem zastępujemy z przez y.
BST – przesuwanie poddrzew – procedura TRANPLANT TRANSPLANT (T,u,v) Ifu.p==NIL T.root=v Elseif u==u.p.left u.p.left=v elseu.p.right =v if v!=NIL v.p = u.p
BST – Usuwanie węzła TREE-DELETE (T,z) Ifz.left==NIL TRANSPLANT(T, z, z.right) Elseifz.right ==NIL TRANSPLANT(T,z,z.left) Else y=TREE-MINIMUM(z.right) Ify.p!=z TRANSPLANT(T,y,y.right) y.right=z.right y.right.p=y TRANPLANT(T,z,y) y.left=z.left y.left.p=y
Bibliografia • Drozdek, C++ Algorytmy i Struktury danych, Helion, Gliwice 2004 • Cormen Thomas; Leiserson Charles; Rivest Ronald; Stein Clifford, „Wprowadzenie do Algorytmów”, Wydawnictwo Naukowe PWN, Warszawa 2012