4.5 KiB
4.5 KiB
Seznam
- abstraktní datová struktura
- umožňuje vložení i odebrání prvku z jakékoliv pozice
Jak specifikovat pozici?
- indexem
- aktuální pozice
Ukazovátko
- ukazuje na aktuální pozici v seznamu
- posun dopředu i na začátek
- operace v
\Theta(1)
- možné interpretace:
- ukazuje na prvek
- některé operace problematické (např. vložení - před nebo za prvek?)
- ukazuje mezi prvky
- jednožnačné vkládání: sem
- vybírání/odebírání dohodou - třeba prvek napravo
- ukazuje na prvek
Implementace spojovou strukturou
- opět spojovací dílek - data a následující prvek
- reference na první (
first
) a aktuální prvek (current
) - ukazovátko míří na
current
acurrent.next
- je-li
current = null
, míří se před první prvek seznamu - pohyb ukazovátka:
moveToFirst
- na začáteknext
- na další prvek- potřeba myslet na posun z
null
a neexistující další prvek
- potřeba myslet na posun z
- přidávání mezi dva prvky:
- přepíše se odkaz
current.next
na vložený prvek - vloženému prvku se nastaví původní
current.next
- přepíše se odkaz
- mazání prvku
- vyřešení speciálních případů (na začátku, na konci)
current.next = current.next.next
, prvek se přeskočí
- vybrání prvku na zadané pozici
- projdeme seznam od začátku
\Omega(n)
složitost!
Iterátor
- seznam neumožňuje prácí na více místech
- vznik odděleného ukazovátka od dat - iterátoru (návrhový vzor)
- třída, která má aktuální index
current
a referenci `list - stejná implementace jako u seznamu (místo
first
se použijelist.first
)`
Operace
- vybrání dat napravo (
get()
) - posunutí doprava (
next()
) - vrácení na začátek (
moveToFirst()
) - přidání prvku na pozici (
insert(...)
) - odstranění prvku napravo (
remove()
) - vytvoření klonu (
clone()
), který ukazuje na stejný prvek
Problémy
- iterátory o sobě nevědí
- jeden smaže prvek, na který odkazuje jiný iterátor
- řešení
- používat více iterátorů pouze pro čtení
- při editaci zajistit, aby nebyl zasažen jiný iterátor
- pohyb pouze dopředu
- řešení: obousměrně zřetězený seznam
- spojovací prvek s
next
iprevious
- v iterátoru nová metoda
previous()
- složitější konzistence struktury
- 2x více režijní paměti
Implementace paralelními poli
- jen dopředné zřetězení
- dvě stejně dlouhá pole
- pro data
- pro index následovníka (
next
) - prázdného či obsazeného prvku
- index začátku
first
- index volného pole
empty
- index aktuálního prvku
current
Kolekce v Javě
Java poskytuje knihovní třídy pro základní implementaci kolekcí
Třída LinkedList<T>
- spojová datová struktura
Operace
- operace v
\mathcal O(1)
void addFirst(T)
,void addLast(T)
T getFirst()
,T getLast()
void removeFirst()
,void removeLast()
- operace v
\Omega(n)
void add(int, T)
- vložení na daný indexT get(int)
- získání z indexuvoid remove(int)
- smazání z indexu
Iterator<T> iterator()
ListIterator<T> listIterator()
- lepší iterátor
Třída ArrayList<T>
- dynamické pole
Operace
- operace v
\mathcal O(1)
void add(T)
T get(int)
- operace v
\Omega(n)
- `void remove(int)
- operace pro získání iterátorů - ale s vyšší složitostí!
Třída Iterator<T>
Operace
T next()
- vrátí přeskočený prvek a posune se dopředu- na konci vyhodí výjimku
boolean hasNext()
- existence dalšího prvkuvoid remove()
- odstraní poslední prvek vrácený metodounext()
- před dalším odstraněním potřeba zavolat
next()
- před dalším odstraněním potřeba zavolat
- chybí:
- návrat na začátek (stačí si vyžádat nový iterátor)
- metoda pro vložení prvku
Vylepšený ListIterator<T>
- má navíc:
T previous()
- vrátí hodnotu předchozího prvku a posune iterátor zpět- na začátku vyhodí výjimku
void add(T)
- vloží prvek na pozici iterátoru
Rozhraní Queue<T>
- rozhraní fronty
LinkedList<T>
toto rozhraní implementuje
Operace
void add()
T element()
T remove()
Třída Stack<T>
implementace zásobníku dynamickým polem - ArrayList
- stejné metody poskytuje i
LinkedList
Operace
void push(T)
T pop()
T peek()
Obecná zásada
Kolekce v čase \mathcal O(1)
umí buď
- vybrat prvek na specifickém indexu, anebo
- vyjmout/vložit prvek na pozici iterátoru,
ale nikdy ne obojí současně!
Najít i-tý prvek ve spojovém seznamu zabere vždy \Omega(n)
.