Trie önek ağacı oluşturma, kelime ekleme-arama-silme O(m), önek arama, otomatik tamamlama DFS — adım adım SVG görseller, karşılaştırma tabloları ve C kodu.
🗂️ Veri Yapıları📅 21 February 2026👁️ 2 görüntülenme
Trie Nedir?
Trie (okunuşu: "tray"), her kenarın bir karakter temsil ettiği özel bir ağaç yapısıdır. Kökten herhangi bir düğüme giden yol bir önek (prefix) oluşturur. Kelime sonunu gösteren düğümler işaretlenir. Adı "retrieval" kelimesinden gelir.
BST / Hash Table ile farkı
BST: Kelime arama O(m × log n) — her karşılaştırma O(m). Hash: Kelime arama O(m) ortalama — ama önek arama yapamaz. Trie: Kelime arama O(m) garanti — ve önek arama da O(m)!
Nerelerde kullanılır?
Otomatik tamamlama (Google arama) Yazım denetimi (spell checker) IP yönlendirme (longest prefix match) T9 klavye / kelime tahmin
Temel fikir: Ortak önekleri paylaş. "araba", "ara", "arı" kelimeleri ilk 2 karakteri (a-r) paylaşır → Trie'de bu 2 karakter tek bir yolda saklanır. n kelime yerine ortak önekler sıkıştırılır → hem bellek hem hız kazancı.
Referans Trie — 5 Kelime
Kelimeler: "araba", "ara", "arı", "bal", "bak"
5 Kelimelik Trie — Paylaşılan Önekler
★ = isEndOfWord=true. "ara" hem kelime hem de "araba"nın öneki — ikisi de aynı yolda.
Trie Düğüm Yapısı
trie.c — yapı ve yardımcılar
#include<stdio.h>#include<stdlib.h>#include<string.h>#include<stdbool.h>#defineALFABE_BOYUTU26// a-z (küçük İngilizce harf)typedefstructTrieNode {
structTrieNode* children[ALFABE_BOYUTU];
bool isEndOfWord; // ★ kelime sonu mu?
} TrieNode;
TrieNode* yeniDugum() {
TrieNode* node = (TrieNode*)calloc(1, sizeof(TrieNode));
// calloc ile tüm children = NULL, isEndOfWord = falsereturn node;
}
/* Karakter → indeks dönüşümü */intcharIndex(char c) {
return c - 'a';
}
Alfabe boyutu seçimi:children[26] sadece küçük İngilizce harfler (a-z) içindir. Türkçe karakterler (ç, ğ, ı, ö, ş, ü) için 32'ye çıkarılabilir veya hash map tabanlı children kullanılabilir. Unicode desteği için genellikle hash map tercih edilir. Bu yazıda kavramsal basitlik için 26 harf kullanıyoruz.
Kelime Ekleme (Insert)
Ekleme, kelimenin her karakteri için kökten başlayarak ağaçta ilerler. Karakter için çocuk yoksa yeni düğüm oluşturur, varsa mevcut düğüme geçer. Son karakterde isEndOfWord = true işaretler.
trieEkle() — O(m), m = kelime uzunluğu
voidtrieEkle(TrieNode* root, constchar* kelime) {
TrieNode* curr = root;
for (int i = 0; kelime[i]; i++) {
int idx = charIndex(kelime[i]);
if (!curr->children[idx])
curr->children[idx] = yeniDugum(); // yeni düğüm
curr = curr->children[idx]; // ilerle
}
curr->isEndOfWord = true; // ★ kelime sonu
}
Adım Adım — 3 Kelime Ekleme
① insert("araba") → ② insert("ara") → ③ insert("arı")
① "araba" — 5 yeni düğüm
② "ara" — 0 yeni düğüm!
③ "arı" — 1 yeni düğüm (ı)
"ara" eklenirken 0 yeni düğüm → sadece isEndOfWord=true. "arı" eklenirken "a-r" yolu paylaşılır, sadece "ı" düğümü yeni.
Kelime Arama (Search)
Arama, kelimenin her karakteri için kökten ilerler. Son karaktere ulaşılırsa veisEndOfWord == true ise kelime bulunmuştur. Düğüm varsa ama isEndOfWord false ise → kelime yok, sadece önek var.
Arama: "ara" ✓ vs "ar" ✗
search("ara") → BULUNDU ✓
kök →a→ →r→ →a→ ★ isEnd=true
search("ar") → BULUNAMADI ✗
kök →a→ →r→ isEnd=false
"ar" düğümü var ama kelime olarak işaretlenmemiş → arama false döner.
trieAra() — kelime tam eşleşmesi O(m)
booltrieAra(TrieNode* root, constchar* kelime) {
TrieNode* curr = root;
for (int i = 0; kelime[i]; i++) {
int idx = charIndex(kelime[i]);
if (!curr->children[idx])
returnfalse; // karakter yok → kelime yok
curr = curr->children[idx];
}
return curr->isEndOfWord; // düğüm var ama kelime mi?
}
Önek Arama (Starts With)
Önek arama, kelime aramanın neredeyse aynısıdır — tek fark: sonda isEndOfWord kontrolü yapılmaz. Önek yolu varsa yeterlidir.
onekVar() — önek sorgulama O(m)
boolonekVar(TrieNode* root, constchar* onek) {
TrieNode* curr = root;
for (int i = 0; onek[i]; i++) {
int idx = charIndex(onek[i]);
if (!curr->children[idx])
returnfalse;
curr = curr->children[idx];
}
returntrue; // isEndOfWord kontrol etmiyoruz!
}
Arama vs Önek:trieAra("ar") → false (kelime yok). onekVar("ar") → true (önek var — "ara", "araba", "arı" bu önekle başlar). Tek satır fark: return curr->isEndOfWord yerine return true.
Kelime Silme (Delete)
Silme, Trie'nin en karmaşık operasyonudur. Bir kelimeyi silerken başka kelimelerin yolunu bozmamaya dikkat etmek gerekir. Üç farklı durum ortaya çıkar:
Durum 1: Başka kelimenin öneki
"ara" sil → "araba" hâlâ var. Sadece isEndOfWord = false yap. Düğüm silinmez.
Durum 2: Benzersiz son ek
"arı" sil → "ı" düğümü başka kelimede yok → düğümü sil. "a-r" yolu "araba" için kalır.
Durum 3: Tamamen benzersiz
"bak" sil → "b-a-k" yolu başka kelimede yoksa (ama "bal" "b-a"yı paylaşır) → sadece "k" düğümünü sil.
Yardımcı: Çocuğu Var mı?
cocuguVar() — düğüm yaprak mı?
boolcocuguVar(TrieNode* node) {
for (int i = 0; i < ALFABE_BOYUTU; i++)
if (node->children[i])
returntrue;
returnfalse;
}
Silme Kodu — Özyinelemeli
trieSil() — rekürsif, O(m)
/* Dönüş: bu düğüm silinmeli mi? */booltrieSilHelper(TrieNode* node, constchar* kelime, int derinlik) {
if (!node) returnfalse;
// Kelimenin sonuna ulaştıkif (derinlik == strlen(kelime)) {
if (!node->isEndOfWord)
returnfalse; // kelime yok
node->isEndOfWord = false; // ★ işareti kaldır// Çocuğu yoksa silinebilirreturn !cocuguVar(node);
}
int idx = charIndex(kelime[derinlik]);
bool silmeli = trieSilHelper(
node->children[idx], kelime, derinlik + 1
);
if (silmeli) {
free(node->children[idx]);
node->children[idx] = NULL;
// Bu düğüm de silinebilir mi?return !node->isEndOfWord && !cocuguVar(node);
}
returnfalse;
}
voidtrieSil(TrieNode* root, constchar* kelime) {
trieSilHelper(root, kelime, 0);
}
Adım Adım — Silme Örnekleri
delete("ara") → Durum 1 | delete("arı") → Durum 2
delete("ara") — sadece isEnd=false
kök→a→r→a ★ →b→a ★ Sonuç: a düğümü kalır (araba için) sadece isEndOfWord=false yapıldı
delete("arı") — düğüm silinir
kök→a→r→ı ★ "ı" çocuğu yok, başka kelime yok düğüm free() ile silinir
Rekürsif geri dönüşte her düğüm "silinmeli mi?" sorusunu yanıtlar: çocuğu yoksa VE kelime sonu değilse → sil.
Otomatik Tamamlama (Autocomplete)
Trie'nin en güçlü özelliği: bir önek verildiğinde o önekle başlayan tüm kelimeleri hızlıca bulmak. Algoritma iki adımdan oluşur:
Autocomplete Algoritması: Adım 1: Önekin son düğümüne git (önek arama ile aynı) → O(p), p = önek uzunluğu. Adım 2: Bu düğümden DFS ile tüm alt ağacı tara, isEndOfWord=true olan yolları topla. Toplam: O(p + k) — k = sonuç sayısı × ortalama kelime uzunluğu.
Adım Adım — Önek "ar"
autocomplete("ar") → 3 sonuç
Sonuç:araarabaarı
otomatikTamamla() — önek + DFS
/* DFS ile alt ağaçtaki tüm kelimeleri topla */voiddfsTara(TrieNode* node, char* buffer, int derinlik) {
if (!node) return;
if (node->isEndOfWord) {
buffer[derinlik] = '\0';
printf(" → %s\n", buffer);
}
for (int i = 0; i < ALFABE_BOYUTU; i++) {
if (node->children[i]) {
buffer[derinlik] = 'a' + i;
dfsTara(node->children[i], buffer, derinlik + 1);
}
}
}
voidotomatikTamamla(TrieNode* root, constchar* onek) {
TrieNode* curr = root;
// Adım 1: Önekin son düğümüne gitfor (int i = 0; onek[i]; i++) {
int idx = charIndex(onek[i]);
if (!curr->children[idx]) {
printf("'%s' öneki bulunamadı.\n", onek);
return;
}
curr = curr->children[idx];
}
// Adım 2: DFS ile tüm sonuçları toplachar buffer[256];
strcpy(buffer, onek);
printf("'%s' ile başlayanlar:\n", onek);
dfsTara(curr, buffer, strlen(onek));
}
Tam Demo Program
Yardımcı: Kelime Sayısı
kelimeSayisi() — Trie'deki toplam kelime
intkelimeSayisi(TrieNode* node) {
if (!node) return0;
int sayac = node->isEndOfWord ? 1 : 0;
for (int i = 0; i < ALFABE_BOYUTU; i++)
sayac += kelimeSayisi(node->children[i]);
return sayac;
}
=== Trie Demo ===
--- Ekleme ---
insert("araba")
insert("ara")
insert("arı")
insert("bal")
insert("bak")
insert("balık")
insert("can")
insert("canan")
Toplam: 8 kelime
--- Arama ---
search("ara") → Bulundu ✓
search("araba") → Bulundu ✓
search("ar") → Yok ✗ ← önek var ama kelime değil
search("bal") → Bulundu ✓
search("bala") → Yok ✗ ← bu kelime eklenmedi
search("can") → Bulundu ✓
--- Önek Arama ---
startsWith("ar") → Var ✓ ← ara, araba, arı başlar
startsWith("ba") → Var ✓ ← bak, bal, balık başlar
startsWith("ca") → Var ✓ ← can, canan başlar
startsWith("de") → Yok ✗ ← hiçbir kelime "de" ile başlamıyor
--- Otomatik Tamamlama ---
'ar' ile başlayanlar:
→ ara
→ araba
→ arı
'ba' ile başlayanlar:
→ bak
→ bal
→ balık
'c' ile başlayanlar:
→ can
→ canan
--- Silme ---
delete("ara")
search("ara") → Yok ✗ ← silindi
search("araba") → Var ✓ ← etkilenmedi
delete("arı")
Silme sonrası autocomplete("ar"):
'ar' ile başlayanlar:
→ araba ← sadece araba kaldı
Kalan: 6 kelime
Karmaşıklık Analizi
Trie operasyonlarının karmaşıklığı kelime uzunluğuna (m) bağlıdır, saklanan toplam kelime sayısından (n) bağımsızdır — bu en büyük avantajıdır.
Operasyon
Zaman
Açıklama
Ekleme (insert)
O(m)
m karakter için m düğüm oluştur/ilerle
Arama (search)
O(m)
m karakter boyunca ilerle + isEnd kontrolü
Önek arama (startsWith)
O(p)
p = önek uzunluğu
Silme (delete)
O(m)
Rekürsif traversal + geriye temizlik
Otomatik tamamlama
O(p + k)
p = önek, k = sonuç karakterleri toplamı
Tüm kelimeleri listele
O(N)
N = tüm düğüm sayısı (DFS)
Trie vs Hash Table vs BST
Özellik
Trie
Hash Table
Dengeli BST
Kelime arama
O(m)
O(m) ort.
O(m log n)
Önek arama
O(p) ✓
Yapamaz ✗
O(m log n)
Otomatik tamamlama
Doğal ✓
Yapamaz ✗
Karmaşık
Sıralı erişim
DFS → sıralı ✓
Sırasız ✗
Inorder ✓
Bellek
Yüksek (pointer dizi)
Düşük
Orta
En kötü arama
O(m) garanti
O(n) çarpışma
O(m log n)
Bellek maliyeti: Her düğümde children[26] = 26 pointer = 26 × 8 byte = 208 byte (64-bit). Çoğu pointer NULL olsa bile yer kaplar. 10.000 kelimelik bir sözlük onbinlerce düğüm → megabyte'larca bellek. Bu sorunu çözmek için sıkıştırılmış varyantlar kullanılır.
Trie Varyantları
Varyant
Fark
Avantaj
Sıkıştırılmış Trie (Radix Tree)
Tek çocuklu düğümleri birleştirir: a→r→a → "ara" tek kenar
Çok daha az düğüm, daha az bellek
Ternary Search Trie
Her düğümde 3 çocuk: less / equal / greater
Bellek tasarrufu, BST+Trie karışımı
Suffix Trie / Tree
Bir string'in tüm soneklerini saklar
Alt-string arama, biyoinformatik (DNA)
DAWG
Directed Acyclic Word Graph — ortak sonekleri de paylaşır
Minimum düğüm sayısı, kelime oyunları
Gerçek Dünya Kullanımları
Uygulama
Kullanım
Google Arama
Arama çubuğunda otomatik tamamlama / öneriler
IDE / Editörler
Kod tamamlama (IntelliSense, autocomplete)
Yazım Denetimi
Sözlükte kelime var mı? Benzer kelimeler?
IP Yönlendirme
Longest prefix match — ağ yönlendirme tabloları
Telefon Rehberi
İsim/numara ile önek araması
Kelime Oyunları
Scrabble — geçerli kelime kontrolü + kelime bulma
Biyoinformatik
DNA dizisi eşleştirme (suffix tree)
Sonuç
Trie, string operasyonlarına özelleşmiş bir ağaç yapısıdır. Kelime uzunluğuna bağlı O(m) garanti arama — sözlükteki kelime sayısından bağımsız. Önek arama ve otomatik tamamlama gibi işlemler doğal olarak desteklenir — hash table ve BST bu konuda yetersiz kalır.
Bellek maliyeti dezavantajdır (her düğümde alfabe boyutu kadar pointer), ancak sıkıştırılmış varyantlar (radix tree, ternary search trie) bu sorunu büyük ölçüde çözer. Google aramasından DNA analizine, IP yönlendirmeden oyun motorlarına kadar geniş bir uygulama alanına sahiptir.
Bölüm 6.6 özeti: Trie = önek paylaşımlı ağaç. Her kenar bir karakter, kökten yol = önek. Düğümde children[ALFABE] + isEndOfWord. Ekleme/arama/silme O(m), m = kelime uzunluğu. Önek arama O(p). Otomatik tamamlama = önek bul + DFS → O(p + k). Hash table önek arayamaz, BST O(m log n). Bellek yüksek → sıkıştırılmış varyantlar (radix, ternary). Kullanım: arama motoru, IDE, yazım denetimi, IP yönlendirme, kelime oyunları.