95 lines
2.8 KiB
Markdown
95 lines
2.8 KiB
Markdown
# Rekursive Datenstrukturen
|
|
|
|
|
|
## [LinkedList](./lists.py)
|
|
|
|
In der Vorlesung wurden uns rekursive Datenstrukturen vorgestellt. Und zwar in der einfachsten Form, einer Liste. Hierbei modellieren wir eine *Node* Datenstruktur die unseren Wert *Value* trägt und auf die nächste *Node* in der Liste verweist durch *next_node*.
|
|
|
|
```python
|
|
from dataclasses import dataclass
|
|
from typing import Optional
|
|
|
|
@dataclass
|
|
class Node[T]:
|
|
value: T
|
|
next_node: Optional['Node[T]']
|
|
```
|
|
|
|
Jetzt wollen wir eine Liste um unsere Node modellieren, hierbei interessiert uns nur die erste Node *__head* und die Länge *__length*.
|
|
|
|
```python
|
|
@dataclass
|
|
class LinkedList[T]:
|
|
|
|
def __post_init__(self):
|
|
self.__head: Optional[Node[T]] = None
|
|
self.__length: int = 0
|
|
```
|
|
|
|
Jetzt geht es darum alle Standardmethoden die eine Liste braucht zu implementieren.
|
|
|
|
## [BinaryTree](./trees.py)
|
|
|
|
Jetzt wollen wir eine andere rekursive Datenstruktur definieren, den binary Tree. Diese besteht immer aus zwei Abzweigungen *right* und *left*.
|
|
|
|
```python
|
|
@dataclass
|
|
class Node[T]:
|
|
value: T
|
|
left: Optional['Node[T]']
|
|
right: Optional['Node[T]']
|
|
```
|
|
|
|
Statt jetzt eine eigene Klasse zu erstellen die `Node` intern benutzt wollen wir einfach einen Typalias erstellen und imperative mit Funktionen statt Methoden arbeiten.
|
|
|
|
```python
|
|
type BinaryTree[T] = Optional[Node[T]]
|
|
```
|
|
|
|
### `traverse`
|
|
|
|
Man kann einen BinaryTree auf drei typische arten ablaufen
|
|
|
|
- Inorder
|
|
- Erst laufen wir Links ab
|
|
- Dann schauen wir uns die Node an
|
|
- Dann laufen wir Rechts ab
|
|
- Preorder
|
|
- Erst schauen wir uns die Node an
|
|
- Dann laufen wir Links ab
|
|
- Dann laufen wir Rechts ab
|
|
- Postorder
|
|
- Erst laufen wir Links ab
|
|
- Dann laufen wir Rechts ab
|
|
- Dann schauen wir uns die Node an
|
|
|
|
Hierfür erstellen wir einen `Enum` um zu unterscheiden wie wir gerade ablaufen
|
|
|
|
```python
|
|
class TraversalType(Enum):
|
|
INORDER, POSTORDER, PREORDER = auto(), auto(), auto()
|
|
```
|
|
|
|
Und jetzt implementieren wir einen Generator der uns einen Iterator erzeugt welcher den BinaryTree in der gegebenen Order abläuft.
|
|
|
|
```python
|
|
def traverse[T](tree: BinaryTree[T], order: TraversalType = TraversalType.INORDER) -> Iterator[T]:
|
|
pass
|
|
```
|
|
|
|
Tipp:
|
|
|
|
Mit `yield from ...` kann man einen ganzen Iterator *yield*en.
|
|
|
|
```python
|
|
def my_range(start: int, end: int) -> Iterator[int]:
|
|
yield from range(start, end)
|
|
```
|
|
|
|
## [BinarySearchTree](./search_trees.py) (*Schwer*)
|
|
|
|
Was wäre wenn wir nun einen Binary Tree haben, dieser aber eine Sortierung hat. Wenn wir ein Element hinzufügen packen wir alle Elemente kleiner nach Links und alle anderen nach Rechts.
|
|
|
|
[Hier eine kleine Visualisierung](https://www.cs.usfca.edu/~galles/visualization/BST.html)
|
|
|
|
Probiert einfach mal rum damit ihr euch die Funktion eines BST vorstellen könnt. Wirklich komplex wird `delete`. |