Hiyerarşik Verilerin Temeli - BST, Heap ve Daha Fazlası
Ağaç, düğümlerden (node) oluşan hiyerarşik (ağaç şeklinde, yukarıdan aşağıya sıralı) bir veri yapısıdır. Her düğüm veri tutar ve bir veya daha fazla alt düğüme (child) işaret eder.
💡 Düğümlerin üzerine gelin
Ağacın en üstündeki düğüm. Parent'ı yoktur, her ağacın tam olarak 1 kökü vardır.
Çocuğu olmayan düğümler. Ağacın "uç noktaları".
Bir düğümün hemen üstündeki düğüm. Her düğümün en fazla 1 parent'ı vardır (kök hariç).
Bir düğümün hemen altındaki düğümler. Bir düğümün 0 veya daha fazla çocuğu olabilir.
Aynı parent'a sahip düğümler. B, C, D kardeştir (hepsi A'nın çocuğu).
İki düğüm arasındaki bağlantı. n düğümlü ağaçta n-1 kenar vardır.
Kökten o düğüme olan kenar sayısı
O düğümden en uzak yaprağa olan kenar sayısı
İkili Ağaç, her düğümün en fazla 2 çocuğu olan özel bir ağaç türüdür. Çocuklar sol (left) ve sağ (right) olarak adlandırılır.
İkili ağaçlar en yaygın ağaç türüdür çünkü basit ve verimlidir. BST (Binary Search Tree) ve Heap ikili ağaçlardır. Arama, sıralama ve önceliklendirme işlemlerinde kullanılır.
Her düğüm 0 ya da 2 çocuğa sahip
✅ Her düğüm 0 veya 2 çocuk
❌ 1 çocuklu
düğüm yok
Tüm seviyeler dolu, yapraklar aynı seviyede
Düğüm sayısı: 2^(h+1) - 1
Yükseklik 2
için: 2³-1 = 7
⭐ Heap bu türdendir! Tüm seviyeler dolu, son seviye soldan sağa dolduruluyor
Son seviye hariç tüm seviyeler tamamen dolu. Son seviye en soldan başlayarak dolduruluyor. Sağda boşluk olabilir, ama solda boşluk olamaz!
Complete binary tree'de boşluk olmadığı için dizi ile verimli temsil edilir:
Her düğümün sol/sağ yükseklik farkı ≤ 1
✅ Arama: O(log n)
Linked List'e dönüşmüş
❌ Arama: O(n)
class TreeNode:
"""Çok çocuklu genel ağaç düğümü"""
def __init__(self, data):
self.data = data
self.children = [] # Çocuklar listesi (0 veya daha fazla)
def add_child(self, child_node):
"""Çocuk ekle"""
self.children.append(child_node)
def __repr__(self):
return f"TreeNode({self.data})"
# Dosya sistemi örneği
root = TreeNode("/")
home = TreeNode("home")
usr = TreeNode("usr")
root.add_child(home)
root.add_child(usr)
user1 = TreeNode("kullanici1")
user2 = TreeNode("kullanici2")
home.add_child(user1)
home.add_child(user2)
bin_dir = TreeNode("bin")
lib_dir = TreeNode("lib")
usr.add_child(bin_dir)
usr.add_child(lib_dir)
print(f"Kök: {root.data}")
print(f"Kökün çocukları: {[c.data for c in root.children]}")
print(f"home'un çocukları: {[c.data for c in home.children]}")
print(f"usr'nin çocukları: {[c.data for c in usr.children]}")
# Ağacı yazdır (DFS - Depth First Search)
def print_tree(node, level=0):
"""Ağacı hiyerarşik yazdır"""
indent = " " * level
print(f"{indent}{node.data}")
for child in node.children:
print_tree(child, level + 1)
print("\n=== Ağaç Yapısı ===")
print_tree(root)
class BinaryTreeNode:
"""İkili ağaç düğümü - max 2 çocuk"""
def __init__(self, data):
self.data = data
self.left = None # Sol çocuk
self.right = None # Sağ çocuk
def __repr__(self):
return f"Node({self.data})"
# İkili ağaç oluştur
# 1
# / \
# 2 3
# / \
# 4 5
root = BinaryTreeNode(1)
root.left = BinaryTreeNode(2)
root.right = BinaryTreeNode(3)
root.left.left = BinaryTreeNode(4)
root.left.right = BinaryTreeNode(5)
print(f"Kök: {root.data}")
print(f"Sol çocuk: {root.left.data}")
print(f"Sağ çocuk: {root.right.data}")
print(f"Sol-Sol: {root.left.left.data}")
print(f"Sol-Sağ: {root.left.right.data}")
# Derinlik hesapla
def depth(node, target_node, current_depth=0):
"""Bir düğümün derinliğini hesapla"""
if node is None:
return -1
if node == target_node:
return current_depth
left_depth = depth(node.left, target_node, current_depth + 1)
if left_depth != -1:
return left_depth
return depth(node.right, target_node, current_depth + 1)
# Yükseklik hesapla
def height(node):
"""Bir düğümün yüksekliğini hesapla"""
if node is None:
return -1 # Boş düğüm
if node.left is None and node.right is None:
return 0 # Yaprak düğüm
left_height = height(node.left)
right_height = height(node.right)
return max(left_height, right_height) + 1
print(f"\n=== Derinlik ve Yükseklik ===")
print(f"Kökün derinliği: {depth(root, root)}")
print(f"Kökün yüksekliği (ağacın yüksekliği): {height(root)}")
print(f"Node 2'nin derinliği: {depth(root, root.left)}")
print(f"Node 2'nin yüksekliği: {height(root.left)}")
print(f"Node 4'ün derinliği: {depth(root, root.left.left)}")
print(f"Node 4'ün yüksekliği: {height(root.left.left)}")
class CompleteBinaryTree:
"""
Complete binary tree - dizi ile temsil
Heap bu yapıyı kullanır!
"""
def __init__(self, data_list):
self.tree = data_list
def parent_index(self, i):
"""i indeksindeki düğümün parent indeksi"""
if i == 0:
return None # Kökün parent'ı yok
return (i - 1) // 2
def left_child_index(self, i):
"""i indeksindeki düğümün sol çocuk indeksi"""
left = 2 * i + 1
return left if left < len(self.tree) else None
def right_child_index(self, i):
"""i indeksindeki düğümün sağ çocuk indeksi"""
right = 2 * i + 2
return right if right < len(self.tree) else None
def get_value(self, i):
"""İndeksteki değeri al"""
return self.tree[i] if 0 <= i < len(self.tree) else None
def print_tree_info(self):
"""Ağaç bilgilerini yazdır"""
print(f"Dizi: {self.tree}")
print(f"Düğüm sayısı: {len(self.tree)}\n")
for i in range(len(self.tree)):
print(f"İndeks {i} (değer: {self.tree[i]}):")
parent_idx = self.parent_index(i)
if parent_idx is not None:
print(f" Parent: indeks {parent_idx} = {self.tree[parent_idx]}")
else:
print(f" Parent: yok (kök düğüm)")
left_idx = self.left_child_index(i)
if left_idx is not None:
print(f" Sol çocuk: indeks {left_idx} = {self.tree[left_idx]}")
else:
print(f" Sol çocuk: yok")
right_idx = self.right_child_index(i)
if right_idx is not None:
print(f" Sağ çocuk: indeks {right_idx} = {self.tree[right_idx]}")
else:
print(f" Sağ çocuk: yok")
print()
# Complete binary tree örneği
# 1
# / \
# 2 3
# / \ / \
# 4 5 6 7
# / \
# 8 9
tree_data = [1, 2, 3, 4, 5, 6, 7, 8, 9]
tree = CompleteBinaryTree(tree_data)
tree.print_tree_info()
Genel yapıdan özele doğru iniyoruz. Her adımda yeni bir kısıtlama ekleniyor.
Düğümler (vertices) ve kenarlar (edges). Herhangi bir düğüm herhangi bir düğüme bağlanabilir.
Örnek: Facebook arkadaşlık ağı, Google Maps yol haritası
Döngüsüz, bağlı graf. Bir kök düğümden başlar, her düğümün tam 1 parent'ı vardır (kök hariç).
Örnek: Dosya sistemi (bir klasör birçok alt klasör/dosya içerir), HTML DOM
Her düğümün en fazla 2 çocuğu: sol çocuk (left) ve sağ çocuk (right).
Neden önemli? Basit ve verimli! BST ve Heap bu yapıdadır.
İkili Ağaç + Sıralama:
Kullanım: Sıralı veri
arama/ekleme/silme
Karmaşıklık: O(log n) - dengeli ise
Complete Binary Tree + Heap özelliği:
Kullanım:
Priority Queue
Karmaşıklık: Min/Max O(1), Ekle/Sil O(log n)
Her düğümün sadece 1 çocuğu var (veya hiç). Ağaç doğrusal hale gelir.
Sonuç: Dengesiz BST Linked List'e dönüşür → O(n) karmaşıklık!
| Özellik | Genel Ağaç | İkili Ağaç | BST | Heap | Linked List |
|---|---|---|---|---|---|
| Çocuk Sayısı | 0 veya daha fazla | 0, 1, veya 2 | 0, 1, veya 2 | 0, 1, veya 2 | 0 veya 1 |
| Özel Kural | - | - | Sol < Kök < Sağ | Parent ≥/≤ Child | - |
| Yapı | Herhangi | Herhangi | Herhangi | Complete | Doğrusal |
| Arama | O(n) | O(n) | O(log n)* | O(n) | O(n) |
| Min/Max | O(n) | O(n) | O(log n)* | O(1) | O(n) |
| Depolama | Pointer | Pointer | Pointer | Dizi | Pointer |
| Kullanım | Dosya sistemi | Genel | Sıralı veri | Priority Queue | Dinamik liste |
* Dengeli ise. Dengesiz BST O(n) olabilir.