ListyDrzewa

advertisement
Zbiory dynamiczne
Zbiór dynamiczny
Zbiór dynamiczny to zbiór wartości
pochodzących z pewnego określonego
uniwersum, którego zawartość zmienia
się w trakcie działania programu.
 Elementy zbioru dynamicznego musimy
co najmniej umieć porównać pod
względem identyczności (czy dwa
elementy są równe albo różne).

Dynamiczne struktury danych
Dynamiczna struktura danych, to struktura
danych pozwalająca na przechowywanie zbioru
dynamicznego; rozmiar tej struktury dostosowuje
się do rozmiaru danych.
 W zbiorze dynamicznym musimy umieć
realizować operację dodania nowego elementu
do zbioru i usunięcia ze zbioru wskazanego
elementu.
 Tablica dynamiczna jest dynamiczną strukturą
danych.
 Zastosowanie: przechowywanie pewnego zbioru
danych, którego zawartość będzie się zmieniać w
trakcie pracy programu.

Słownik

Słownik (ang. dictionary) to struktura danych
pozwalająca efektywnie realizować następujące
operacje:
◦ insert(x) – dodanie nowego elementu x do zbioru
dynamicznego,
◦ delete(x) – usunięcie elementu o wartości x ze zbioru
dynamicznego,
◦ search(x) – sprawdzenie czy w zbiorze dynamicznym
znajduje się element o wartości x.
Multizbiór to zbiór dynamiczny, w którym mogą
się powtarzać elementy o takich samych
wartościach.
 Struktura danych jest homogeniczna, jeśli składa
się z elementów tego samego typu.

Listy
Lista





Lista (ang. list) to homogeniczna struktura danych służąca do
reprezentowania zbioru dynamicznego, w której elementy
ułożone w ciąg.
Element listy nazywa się węzłem (ang. node); każdy węzeł
zawiera pole info służące do przechowywania wartości z
pewnego określonego uniwersum oraz pole next ze
wskaźnikiem na następny element listy (ostatni element listy
ma w polu next wpisany wskaźnik pusty).
Pierwszy węzeł listy jest nazywany głową (ang. head) albo
początkiem listy.
Dostęp do elementów listy jest sekwencyjny – a więc dojście
do elementu k-tego wymaga przejścia przez kolejne
elementy listy od pierwszego do docelowego.
Zastosowanie: lista najlepiej nadaje się do danych, które
będą przetwarzane sekwencyjnie.
Lista
Lista jednokierunkowa (ang. single linked
list) to lista, po której można się poruszać
tylko od głowy do ogona – w każdym
węźle jest tylko wskaźnik do następnika.
 Lista dwukierunkowa (ang. double linked
list) to lista, po której można się poruszać
w obu kierunkach: w stronę głowy i w
stronę ogona – w każdym węźle są dwa
wskaźniki next do następnika i prev do
poprzednika.

Lista





Lista cykliczna to lista, w której ostatni węzeł
posiada wskaźnik do pierwszego węzła.
Lista dwukierunkowa może być cykliczna.
Lista z wartownikiem to lista, w której na końcu
umieszczony jest węzeł zwany wartownikiem –
wartownik nie przechowuje danych, pełni rolę
pomocniczą w nawigacji po liście.
Lista z wartownikiem może być cykliczna lub
dwukierunkowa.
Gdy dane pochodzą z uniwersum z porządkiem
liniowym, to dane w liście można przechowywać
w sposób uporządkowany – mamy w tedy do
czynienia z listą uporządkowaną.
Lista
Wyszukiwanie wartości w liście – wersja iteracyjna
Search(wezeł *w, x) -> boolean
{
while (w.info != x) {
if (w.next != null) w := w.next;
else return false;
}
return true;
}
Czas: O(n) gdzie n to ilość elementów na liście
Pamięć: O(1)
Lista
Wyszukiwanie wartości w liście – wersja rekurencyjna
Search(wezeł *w, x) -> boolean
{
if (w == null) return false;
if (w.info == x) return true;
return Search(w.next, x);
}
Czas: O(n) gdzie n to ilość elementów na liście
Pamięć: O(n) zależy od liczby wywołań rekurencyjnych
Lista
Wyszukiwanie wartości w liście posortowanej – wersja
rekurencyjna
Search(wezeł *w, x) -> boolean
{
if (w == null) return false;
if (w.info == x) return true;
if (w.info > x) return false;
return Search(w.next, x);
}
Czas: O(n) gdzie n to ilość elementów na liście
Pamięć: O(n) zależy od liczby wywołań rekurencyjnych
Lista

Wstawianie elementu do listy
nieuporządkowanej:
◦ na początek listy,
◦ na koniec,
◦ na zadaną pozycję.
Wstawianie elementu do listy
uporządkowanej
 Usuwanie elementu z listy:

◦ usuwanie elementu z zadanej pozycji,
◦ usuwanie elementu o zadanej wartości.
Listy


Technika zwracania wskaźnika do struktury po zmodyfikowaniu.
Przykład: wstawienie elementu na zadaną pozycję:
insert(węzeł *w, x, pos) -> węzeł*
{
if (pos < 0) error;
if (w == null and pos > 0) error;
if (pos > 0) {
w.next := insert(w.next, x, pos-1);
return w;
}
else return new węzeł(x, w);
}
wywołanie:
head := insert(head, x);
Listy

Operacje słownikowe na liście nelementowej wymagają:
◦ czasu O(n),
◦ pamięci O(1) gdy używamy iteracji albo O(n)
gdy korzystamy z rekurencji.
Listy



Zadanie: podział listy na dwie równe podlisty (z dokładnością do 1
elementu).
Rozwiązanie: dwa wskaźniki, jeden robi podwójne skoki, drugi
pojedyncze; po dotarciu na koniec listy pierwszego wskaźnika, drugi
wskazuje na węzeł środkowy.
split(węzeł *h) -> (węzeł*, węzeł*)
{
węzeł *p = head;
węzeł *q = head;
if (p == null) return (null, null);
while (q != null) {
q := q.next;
if (q == null) break;
q := q.next;
if (q != null) p := p.next;
}
q := p.next;
p.nex; := null;
return (head, q);
}
Listy



Zadanie: należy scalić dwie posortowane lisy .
Rozwiązanie: do końca listy wynikowej doczepiamy mniejszy spośród głów pozostałych
list.
merge(węzeł *g, węzeł *h) -> węzeł*
{
if (g == null) return h;
if (h == null) return g;
węzeł *r = g;
if (g.info < h.info) g := g.next;
else { r := h; h := h.next; }
węzeł *s = r;
while (g != null and h != null)
if (g.info < h.info) {
r.next := g;
r := r.next;
g := g.next;
}
else {
r.next := h;
r := r.next;
h := h.next;
}
if (g != null) r.next := g;
else r.next := h;
return s;
}
Zadanie 1
Zdefiniuj klasę reprezentującą węzeł listy
jednokierunkowej wraz z operacjami
wstawiania nowego elementu na zadaną
pozycję, usuwania węzła z zadanej pozycji
i sprawdzania czy określony element
znajduje się na liście (operacje te
zaimplementuj rekurencyjnie).
 Węzeł listy zdefiniuj jako szablon przy
pomocy template<typename T>.

Zadanie 2


Zdefiniuj klasę reprezentującą listę jako
opakowanie dla struktury zbudowanej na
węzłach. W klasie tej opakuj metodę
wstawiającą do listy, usuwającą z listy i
sprawdzającą czy element występuje na
liście. Dodatkowo dopisz metody:
wstawiająca element na początek listy, na
koniec listy, usuwającą pierwszy i usuwającą
ostatni element na liście.
Listę zdefiniuj jako szablon przy pomocy
template<typename T>.
Zadanie 3
Zdefiniuj klasę reprezentującą węzeł listy
jednokierunkowej uporządkowanej wraz z
operacjami wstawiania nowego elementu,
usuwania elementu i sprawdzania czy określony
element znajduje się na liście.
 Zdefiniuj też klasę reprezentującą listę jako
opakowanie dla struktury zbudowanej na
węzłach.
 Węzeł listy oraz listę zdefiniuj jako szablony przy
pomocy template<typename T>.

Drzewa
Drzewa






Drzewo (ang. tree) to zbiór węzłów powiązanych
wskaźnikami, bez cykli.
Drzewo ukorzenione – posiada wyróżniony węzeł
początkowy zwany korzeniem (ang. root).
Drzewo jest strukturą hierarchiczną – analogia do
polskich jabłek na rozgałęzionej jabłoni.
Korzeń węzła znajduje się na poziomie 0; numer
poziomu danego węzła w drzewie jest wyznaczony
odległością krawędziową od korzenia.
Liściem w drzewie (ang. leaf) jest węzeł bez żadnego
następnika.
Węzeł wewnętrzny posiada co najmniej jednego
następnika.
Drzewa
Wysokość drzewa – długość najdłuższej
ścieżki od korzenia do liścia (liczba
węzłów) – inaczej liczba poziomów na
których zapisane jest drzewo.
 Głębokość węzła – długość ścieżki od
korzenia do tego węzła (liczba węzłów) –
inaczej numer poziomu na którym
znajduje się węzeł.
 Uwaga: poziomy w drzewie numerujemy
od 0; korzeń znajduje się na poziomie 0.

Drzewo
Drzewo uporządkowane ma
ponumerowanych (oznaczonych)
następników – ich kolejność w takim
drzewie jest istotna.
 Drzewo k-arne to drzewo
uporządkowane, które posiada co
najwyżej k następników.
 Drzewo binarne – może posiadać dwóch
następników: lewego i prawego.

Reprezentacja drzew uporządkowanych
„na lewo syn na prawo brat”
T.root
/
/
/
/
/
/
/
/
/
/
/
/
/
Drzewo BST
Drzewo BST to drzewo binarne, w którym
przechowujemy elementy z pewnego
uniwersum z porządkiem liniowym i w
drzewie tym jest zachowany porządek
symetryczny.
 W drzewie binarnym jest zachowany
porządek symetryczny, gdy elementy
mniejsze od korzenia znajdują się w lewym
poddrzewie, elementy większe od korzenia
w prawym poddrzewie oraz w lewym i w
prawym poddrzewie też jest zachowany
porządek symetryczny.

Drzewo BST

Wyszukiwanie w drzewie BST – działa tak jak
w wyszukiwaniu binarnym
serach (węzeł *w, x) -> boolean
{
if (w == null) return false;
if (x < w.info)
return search(w.left, x);
else if (w.info < x) then
return search(w.right, x);
else return true;
}
Drzewo BST
Element minimalny znajduje się
najbardziej po lewej stronie w drzewie
BST – od korzenia poruszamy się ciągle w
lewo, ostatni węzeł na lewej ścieżce
zawiera minimum.
 Element maksymalny znajduje się
najbardziej po prawej stronie w drzewie
BST – od korzenia poruszamy się ciągle w
prawo , ostatni węzeł na prawej ścieżce
zawiera maksimum.

Drzewo BST

Wstawienie nowego elementu do drzewa BST –
znajdujemy mu miejsce tak jak w wyszukiwaniu
binarnym (aż dojdziemy do wskaźnika pustego)
insert (węzeł *w, x) -> node*
{
if (w == null) return new node(x);
if (x < w.info)
w.left := insert(w.left, x);
else if (w.info < x) then
w.right := insert(w.right, x);
else w.info := x;
return w;
}
Drzewo BST

Usunięcie elementu z drzewa BST –
znajdujemy miejsce tego elementu tak jak w
wyszukiwaniu binarnym (jak dojdziemy do
wskaźnika pustego to elementu nie ma w
drzewie):
◦ gdy element jest w liściu, to odcinam liść;
◦ gdy element jest w węźle z jednym następnikiem,
to usuwam ze ścieżki ten węzeł;
◦ gdy element jest w węźle z dwoma synami, to z
prawego poddrzewa usuwamy minimum (albo z
lewego poddrzewa usuwamy maksimum) i
przenosimy je do tego węzła.
Przeglądanie drzew BST
In-order – najpierw jest przeglądane i
przetwarzane lewe poddrzewo w porządku
in-order, potem korzeń a na końcu prawe
poddrzewo w porządku in-order.
 Przeglądanie drzewa BST w porządku inorder gwarantuje, że węzły tego drzewa
zostaną przetworzone w kolejności od
najmniejszej do największej wartości.
 Przeglądanie in-order można wykorzystać do
sortowania danych.

Przeglądanie drzew BST
Pre-order – najpierw jest przeglądany i przetwarzany
korzeń drzewa, potem lewe poddrzewo w porządku
pre-order a na końcu prawe poddrzewo w porządku
pre-order.
 Przeglądanie drzewa BST w porządku pre-order
gwarantuje, że na początku będą przetworzone węzły
z lewej ścieżki tego drzewa w kolejności od korzenia
do węzła o najmniejszej wartości.
 Post-order – najpierw jest przeglądane i przetwarzane
lewe poddrzewo w porządku post-order, potem
prawe poddrzewo w porządku post-order a na końcu
korzeń drzewa.
 Przeglądanie drzewa BST w porządku post-order
gwarantuje, że na końcu będą przetworzone węzły z
prawej ścieżki tego drzewa w kolejności od węzła o
największej wartości do korzenia.

Struktura drzew BST



Wysokość drzewa – liczba poziomów
zajmowanych przez drzewo (korzeń znajduje
się na poziomie 0, na ostatnim poziomie
znajdują się tylko liście) – albo długość
wierzchołkowa najdłużej ścieżki od korzenia
do liścia .
Głębokość węzła – numer poziomu na
którym znajduje się dany węzeł – albo
krawędziowa odległość od korzenia.
Wysokość drzewa n-elementowego jest nie
mniejsza niż log(n) i nie większa niż n.
Struktura drzew BST





Drzewo regularne to drzewo binarne, w
którym każdy węzeł wewnętrzny na dwóch
synów.
Drzewo pełne to drzewo regularne, w
którym wszystkie liście są na tym samym
poziomie.
Drzewo pełne o wysokości h ma 2h-1
węzłów.
Drzewo zupełne to drzewo pełne, z którego
usunięto część liści z prawej strony.
Drzewo zupełne o wysokości h ma od 2h-1 do
2h-1 węzłów.
Rotacje w drzewie BST

W dowolnym wewnętrznym węźle drzewa
BST można zrobić rotację (w lewo albo w
prawo) i nie zostanie zniszczony porządek
symetryczny w tym drzewie.
Zadanie 1
Zdefiniuj klasę reprezentującą węzeł
drzewa BST wraz z operacjami wstawiania
nowego elementu, usuwania elementu i
sprawdzania czy określony element
znajduje się w drzewie.
 Węzeł drzewa zdefiniuj jako szablon przy
pomocy template<typename T>.

Zadanie 2


Zdefiniuj klasę reprezentującą drzewo BST
jako opakowanie dla struktury zbudowanej
na węzłach. W klasie tej opakuj metodę
wstawiającą do drzewa, usuwającą z drzewa
i sprawdzającą czy element występuje w
drzewie. Dodatkowo dopisz metody:
wyznaczające oraz usuwające najmniejszy i
największy element w drzewie.
Drzewo BST zdefiniuj jako szablon przy
pomocy template<typename T>.
Download