hierarhické usporiadnie dát:
nadradený prvok
jeden alebo viac podradenných prvkov.
Adresárová štruktúra operačných systémov prirodzene vytvára strom.
C:
├── Dokumenty
│ ├── škola.docx
│ └── projekt.pdf
├── Obrázky
└── Hudba
Každý adresár môže obsahovať ďalšie podadresáre a súbory.
Hierarchia zamestnancov vo firme:
Riaditeľ
├── Ekonomické oddelenie
├── IT oddelenie
└── Personálne oddelenie
HTML dokument je reprezentovaný stromovou štruktúrou.
<html>
<body>
<h1>Nadpis</h1>
<p>Text</p>
</body>
</html>
DOM strom:
html
└── body
├── h1
└── p
Databázy používajú stromové indexy (B-stromy, B+ stromy) na rýchle vyhľadávanie údajov.
Bez stromov by vyhľadávanie v miliónoch záznamov bolo výrazne pomalšie.
Rozhodovacie stromy sa používajú pri:
Príklad:
Je vek > 18?
├── Áno → Dospelý
└── Nie → Dieťa
Pri preklade programu vznikajú syntaktické stromy.
Výraz a + b * c je reprezentovaný ako:
+
/ \
a *
/ \
b c
Výraz (a + b) * (c - d):
*
/ \
+ -
/ \ / \
a b c d
Assign
├── name: x
└── BinaryOp (+)
├── Literal(3)
└── Literal(4)
Python ast modul:
import ast
tree = ast.parse("a + b * c")
print(ast.dump(tree))
Document
├── Heading(level=1, text="Nadpis")
├── Paragraph(text="Nejaký text.")
├── List
│ ├── ListItem(text="položka 1")
│ └── ListItem(text="položka 2")
└── Heading(level=2, text="Podnadpis")
Má > 5 rokov praxe?
├── Áno → Zamestnanec
└── Nie
├── Je študent?
│ ├── Áno → Študent
│ └── Nie → nezamestnaný
└── Nie → nezamestnaný
Uzol - dáta tkoré ukladáme
hrana - vzťah podriadenosti medzi uzlami
vťah nadradenosoti nie je definovaný.
A
/ | \
B C D
/ \
E F
Každý uzol má najviac jedného rodiča, ale môže mať ľubovoľný počet potomkov.
A <- koreň (root)
/ | \
B C D <- B, C, D sú synovia (children) A
/ \ \
E F H <- E, F, H sú listy (leaves) – nemajú potomkov
A
/ | \
B C D
/ \
E F
AB je rodič EE je potomok BB, C, DC, D, E, FA, B A
/ | \
B C D
/ \
E F
A je 0E je 2B je 1A je 2Najznámejším a najjednoduchším špeciálnym prípadom stromu je binárny strom.
Binárny strom je strom, v ktorom môže mať každý uzol najviac dvoch potomkov:
Príklad:
A
/ \
B C
/ \ \
D E F
class Node:
def __init__(self, value):
self.value = value
self.left = None
self.right = None
root = Node("A")
root.left = Node("B")
root.right = Node("C")
root.left.left = Node("D")
root.left.right = Node("E")
root.right.right = Node("F")
Špeciálnym prípadom binárneho stromu je Binary Search Tree.
Pre každý uzol platí:
ľavý podstrom < uzol < pravý podstrom
Príklad:
50
/ \
30 70
/ \ / \
20 40 60 80
Výhody:
def search(node, value):
if node is None:
return False
if node.value == value:
return True
if value < node.value:
return search(node.left, value)
return search(node.right, value)
Zložitosť závisí od výšky stromu:
def insert(node, value):
if node is None:
return Node(value)
if value < node.value:
node.left = insert(node.left, value)
elif value > node.value:
node.right = insert(node.right, value)
return node
Rovnako ako vyhľadávanie má zložitosť O(h).
Prechádzanie stromu znamená systematicky navštíviť všetky jeho uzly.
DFS – Depth-First Search
BFS – Breadth-First Search
Prechádzanie stromu znamená systematicky navštíviť všetky jeho uzly.
Pre strom:
A
/ \
B C
/ \ \
D E F
Výstup: A → B → D → E → C → F
Pri BST získame prvky v usporiadanom poradí.
Výstup: D → B → E → A → C → F
Výstup: D → E → B → F → C → A
def preorder(node):
if node is None:
return
print(node.value)
preorder(node.left)
preorder(node.right)
def inorder(node):
if node is None:
return
inorder(node.left)
print(node.value)
inorder(node.right)
def postorder(node):
if node is None:
return
postorder(node.left)
postorder(node.right)
print(node.value)
Prechádzanie po úrovniach navštevuje uzly zhora nadol a zľava doprava.
Používa sa rad (queue).
A
/ \
B C
/ \ \
D E F
Výstup: A → B → C → D → E → F
from collections import deque
def level_order(root):
if root is None:
return
queue = deque([root])
while queue:
node = queue.popleft()
print(node.value)
if node.left is not None:
queue.append(node.left)
if node.right is not None:
queue.append(node.right)
V mnohých praktických aplikáciách nestačia iba dvaja potomkovia. Preto používame n-árne stromy.
N-árny strom je strom, v ktorom môže mať každý uzol až n potomkov.
Príklad:
A
/ | \
B C D
/ | \ |
E F G H
class Node:
def __init__(self, value):
self.value = value
self.children = []
root = Node("A")
b = Node("B")
c = Node("C")
d = Node("D")
b.children.extend([Node("E"), Node("F"), Node("G")])
d.children.append(Node("H"))
root.children.extend([b, c, d])
def traverse(node):
if node is None:
return
print(node.value)
for child in node.children:
traverse(child)
Výstup: A → B → E → F → G → C → D → H
Rekurzívne prechádzanie je jednoduché, ale pre hlboké stromy môže dôjsť k pretečeniu zásobníka (stack overflow).
Iteratívne prechádzanie používa zásobník (stack) namiesto rekurzie.
Rekurzia aj iteratívny preorder používajú zásobník na ukladanie uzlov, ktoré ešte treba spracovať.
Volací rámec rekurzie je na zásobníku OS:
traverse(A) # stack: [A]
traverse(B) # stack: [A, B]
traverse(E) # stack: [A, B, E]
Python má limit rekurzie (zvyčajne 1000):
import sys
print(sys.getrecursionlimit()) # 1000
Pre hlboký strom → RecursionError.
Iteratívny prístup → zásobník je na hromade (heap), nie vo volacom zásobníku (call stack).
def traverse_iterative(root):
if root is None:
return
stack = [root]
while stack:
node = stack.pop()
print(node.value)
for child in reversed(node.children):
stack.append(child)
Výhody:
V praxi sa používajú špecializované stromy optimalizované pre konkrétne úlohy.
Bežné binárne stromy nie sú vhodné pre databázové indexy – diskové operácie sú pomalé a strom by bol príliš hlboký.
B-strom – každý uzol má viac ako dvoch potomkov:
[15]
/ | \
[5,10] [25] [40]
/ | \ / \ / \
2 8 13 18 22 35 45
Vlastnosti:
B+-strom – údaje sú len v listoch, listy sú prepojené → efektívny range query.
Samovyvážený binárny vyhľadávací strom. Každý uzol je červený alebo čierny.
Pravidlá:
Dôsledok: žiadna cesta nie je viac ako 2× dlhšia ako iná → strom je takmer vyvážený.
Operácie: O(log n). Použitie: Java TreeMap/TreeSet, Linux kernel.