Bölüm 5.3: Trie Veri Yapısı

Algoritmalar
Önek ağacıDüğüm yapısıEklemeArama ve önek sorgusu Otomatik tamamlamaSilmeKarmaşıklık ve bellekSıkıştırılmış ve sonek trie'leri
Sercan KÜLCÜ · Giresun Üniversitesi · Bilgisayar Mühendisliği

Giriş

Öğrenme Çıktıları

Yapı

Trie'nin kök, düğüm, kenar ve kelime sonu işaretinden oluşan yapısını açıklamak; bir kelime kümesinin trie'sini çizmek.

İşlemler

Ekleme, arama, önek sorgusu, otomatik tamamlama ve silme işlemlerini Java ile yazmak ve adım adım izlemek.

Analiz

İşlemlerin O(L) zamanını ve O(N · M) belleğini gerekçelendirmek; trie'yi hash tablosu ve BST ile karşılaştırmak.

Türevler

Sıkıştırılmış trie, sonek trie'si ve Aho–Corasick'in hangi sorunu çözdüğünü açıklamak.

Trie Kavramı

Trie (Önek Ağacı, Prefix Tree)

  • Dizgileri (string) hızlı arama yapılabilecek biçimde saklayan ağaç benzeri bir veri yapısıdır.
  • Kök düğüm boştur; kökten aşağı her adım bir karakteri temsil eder.
  • Kelimeler, kökten başlayan yollarla saklanır; ortak önekler (prefix) tek kez saklanır.
  • Kullanım alanları: otomatik tamamlama, yazım denetimi, IP yönlendirme.
kökandandtantdaddadodo
Ad, bilgi geri getirme anlamındaki İngilizce retrieval sözcüğünden gelir (E. Fredkin, 1960). Çift çember: o düğümde bir kelime biter.

Trie Kavramı

Neden Trie?

Bir sözlükte "an" ile başlayan tüm kelimeleri bulmak istiyoruz. Farklı yapılar bu soruya nasıl yanıt verir?

Dizi / listeHer kelime tek tek karşılaştırılır: n kelime için O(n · L). Kelime sayısı büyüdükçe yavaşlar.
Hash tablosuTam kelimeyi ortalama O(L)'de bulur, ama önek sorgusu yapamaz: "an" ile "and"in hash değerleri ilişkisizdir.
TrieÖnce "a", sonra "n" dalına inilir (2 adım); o düğümün altındaki her kelime "an" ile başlar.
Trie'de arama süresi saklanan kelime sayısına değil, aranan kelimenin uzunluğuna (L) bağlıdır.
danoatddkökan

Trie Kavramı

Temel Bileşenler

Kök (root)Boş düğüm; hiçbir karakteri temsil etmez, her aramanın başlangıcıdır.
Düğümler (nodes)Kökten o düğüme kadarki yol bir önektir; her düğüm çocuk düğümlere bağlantılar tutar.
Kenarlar (edges)Düğümler arası bağlantılardır; her kenar bir karakter taşır.
Kelime sonu işaretikelimeSonu (end of word): bu düğümde bir kelimenin bittiğini belirtir.
kökandandtantdaddadodo

Renkli düğüm "an" önekini temsil eder; "an" bir kelime değildir (işaretsiz), ama "and" ve "ant"in önekidir.

Trie Kavramı

Örnek: Yedi Kelimelik Trie

Kelimeler
ab · aba · abc
ad
ba · bad · bag

7 kelime, toplam 18 karakter → yalnızca 9 düğüm (+ kök).

kökababaabacabcdadbabadbadgbag
"ab" hem bir kelime hem de "aba" ile "abc"nin önekidir. Yalnızca yapıya bakarak bunu anlayamayız; bu yüzden kelimeSonu işareti şarttır.

Trie Kavramı

Örnek: Değer Taşıyan Trie

AnahtarDeğerAnahtarDeğer
A15tea3
i11ted4
in5ten12
inn9to7
kökA15i11n5n9tea3d4n12o7
Trie bir sözlük (map) gibi de kullanılabilir: kelimeSonu yerine düğümde bir değer saklanır. "t" ve "te" değer taşımaz; yalnızca önektir. Büyük harf "A", 26 küçük harflik dizi yerine daha geniş bir alfabe (ör. HashMap) gerektirir.

Uygulama

Trie Düğüm Sınıfı

public class TrieDugumu {
  TrieDugumu[] cocuk;  // 'a'..'z'
  boolean kelimeSonu;  // kelime biter mi?

  public TrieDugumu() {
    kelimeSonu = false;
    cocuk = new TrieDugumu[26];
  }
}
  • Her düğüm 26 çocuk referansı tutar; başlangıçta hepsi null.
  • Karakter düğümde saklanmaz: hangi karakter olduğu, düğümün ebeveyninin hangi indisinde durduğundan anlaşılır.
  • kelimeSonu: kökten buraya gelen yol bir kelime mi?
kelimeSonufalseharfindisa0ab1⁄c2⁄d3de4⁄f5⁄g6⁄h7⁄i8⁄j9⁄k10⁄l11⁄m12⁄n13⁄o14⁄p15⁄q16⁄r17⁄s18⁄t19⁄u20⁄v21⁄w22⁄x23⁄y24⁄z25⁄

and, ant, dad, do eklendikten sonra kökün cocuk dizisi: 26 hücreden yalnızca 2'si dolu (⁄ = null).

Uygulama

Karakterden İndise: ch − 'a'

chUnicodech − 'a'
'a'970
'd'1003
'n'11013
'o'11114
't'11619
'z'12225
031325ad'a' − 'a' = 0, 'd' − 'a' = 3kök.cocuk: diğer 24 hücre null
  • Java'da char bir sayıdır; ch - 'a' küçük harfi 0..25 aralığına eşler.
  • Çocuğa erişim dizi indisiyle O(1)'dir: dugum.cocuk[ch - 'a'].
  • "and" için sırasıyla cocuk[0], cocuk[13], cocuk[3] kullanılır.
'A' - 'a' = −32, 'ç' - 'a' = 134 → ArrayIndexOutOfBoundsException. Büyük harf ve Türkçe karakter için girdi dönüştürülmeli ya da HashMap kullanılmalı.

Ekleme

Ekleme (Insert)

public void ekle(String kelime) {
  TrieDugumu dugum = kok;
  for (char ch : kelime.toCharArray()) {
    int i = ch - 'a';
    if (dugum.cocuk[i] == null)
      dugum.cocuk[i] = new TrieDugumu();
    dugum = dugum.cocuk[i];
  }
  dugum.kelimeSonu = true;
}
  1. Kökten başlanır.
  2. Kelimenin her karakteri için ilgili çocuğa bakılır: cocuk[ch − 'a'].
  3. Çocuk null ise yeni düğüm oluşturulur; varsa (ortak önek) mevcut düğüm kullanılır.
  4. O çocuğa ilerlenir.
  5. Karakterler bitince son düğümde kelimeSonu = true yapılır.
Zaman: O(L) (L = kelime uzunluğu); en fazla L yeni düğüm oluşur. Aynı kelimeyi iki kez eklemek yapıyı değiştirmez.

Ekleme · Adım adım

Ekleme Adım Adım: and, ant, dad, do

kök

Başlangıçta trie yalnızca kökten oluşur; kökün 26 çocuğunun hepsi null, kelimeSonu = false.

köka

"and" ekleniyor, karakter 'a': cocuk[0] null → yeni düğüm oluşturulur ve ona ilerlenir.

kökan

"and" ekleniyor, karakter 'n': cocuk[13] null → yeni düğüm oluşturulur ve ona ilerlenir.

kökand

"and" ekleniyor, karakter 'd': cocuk[3] null → yeni düğüm oluşturulur ve ona ilerlenir.

kökandand

Karakterler bitti: son düğümde kelimeSonu = true (çift çember). "and" eklendi. Kökten itibaren cocuk[0] → cocuk[13] → cocuk[3] yolu kuruldu.

kökandand

"ant" ekleniyor, karakter 'a': cocuk[0] dolu → mevcut "a" düğümüne ilerlenir (ortak önek).

kökandand

"ant" ekleniyor, karakter 'n': cocuk[13] dolu → mevcut "an" düğümüne ilerlenir (ortak önek).

kökandandt

"ant" ekleniyor, karakter 't': cocuk[19] null → yeni düğüm oluşturulur ve ona ilerlenir.

kökandandtant

Karakterler bitti: son düğümde kelimeSonu = true (çift çember). "ant" eklendi. Yalnızca 1 yeni düğüm gerekti: "an" öneki paylaşıldı.

kökandandtantd

"dad" ekleniyor, karakter 'd': cocuk[3] null → yeni düğüm oluşturulur ve ona ilerlenir.

kökandandtantda

"dad" ekleniyor, karakter 'a': cocuk[0] null → yeni düğüm oluşturulur ve ona ilerlenir.

kökandandtantdad

"dad" ekleniyor, karakter 'd': cocuk[3] null → yeni düğüm oluşturulur ve ona ilerlenir.

kökandandtantdaddad

Karakterler bitti: son düğümde kelimeSonu = true (çift çember). "dad" eklendi.

kökandandtantdaddad

"do" ekleniyor, karakter 'd': cocuk[3] dolu → mevcut "d" düğümüne ilerlenir (ortak önek).

kökandandtantdaddado

"do" ekleniyor, karakter 'o': cocuk[14] null → yeni düğüm oluşturulur ve ona ilerlenir.

kökandandtantdaddadodo

Karakterler bitti: son düğümde kelimeSonu = true (çift çember). "do" eklendi. Son durum: 4 kelime, 11 karakter, kök hariç 8 düğüm.

Ekleme · Kodu adım adım çalıştır

ekle("ant"): Kodu Adım Adım İzle

kökandand

Trie'de yalnızca "and" var. dugum kökten başlar.

kökandand

Karakter 'a' → i = 'a' − 'a' = 0.

kökandand

dugum.cocuk[0] null değil → yeni düğüm gerekmez.

kökandand

dugum artık "a" düğümü.

kökandand

Karakter 'n' → i = 'n' − 'a' = 13.

kökandand

dugum.cocuk[13] null değil → yeni düğüm gerekmez.

kökandand

dugum artık "an" düğümü.

kökandand

Karakter 't' → i = 't' − 'a' = 19.

kökandand

dugum.cocuk[19] == null → koşul doğru.

kökandandt

Yeni bir TrieDugumu oluşturulup cocuk[19]'e bağlanır (kelimeSonu = false).

kökandandt

dugum artık "ant" düğümü.

kökandandtant

Döngü bitti: dugum.kelimeSonu = true. "ant" eklendi; yalnızca 1 düğüm oluşturuldu.

public void ekle(String kelime) {
  TrieDugumu dugum = kok;
  for (char ch : kelime.toCharArray()) {
    int i = ch - 'a';
    if (dugum.cocuk[i] == null)
      dugum.cocuk[i] = new TrieDugumu();
    dugum = dugum.cocuk[i];
  }
  dugum.kelimeSonu = true;
}

Arama

Arama (Search)

public boolean ara(String kelime) {
  TrieDugumu dugum = kok;
  for (char ch : kelime.toCharArray()) {
    int i = ch - 'a';
    if (dugum.cocuk[i] == null)
      return false;
    dugum = dugum.cocuk[i];
  }
  return dugum.kelimeSonu;
}
  • Kökten başlanır.
  • Her karakter için ilgili dala (çocuğa) gidilir.
    • Dal yoksa (null) kelime trie'de yoktur.
  • Kelimenin sonuna gelince kelimeSonu denetlenir:
    • true → kelime bulundu;
    • false → kelime yok; yalnızca var olan bir kelimenin önekidir.
Zaman: O(L). Trie'de bir milyon kelime de olsa "dad" en fazla 3 adımda bulunur.

Arama · Kodu adım adım çalıştır

ara("dad"): Kodu Adım Adım İzle

kökandandtantdaddadodo

Trie: and, ant, dad, do. dugum kökten başlar.

kökandandtantdaddadodo

Karakter 'd' → i = 3.

kökandandtantdaddadodo

cocuk[3] null değil → dal var, devam.

kökandandtantdaddadodo

dugum = "d".

kökandandtantdaddadodo

Karakter 'a' → i = 0.

kökandandtantdaddadodo

cocuk[0] null değil → dal var, devam.

kökandandtantdaddadodo

dugum = "da".

kökandandtantdaddadodo

Karakter 'd' → i = 3.

kökandandtantdaddadodo

cocuk[3] null değil → dal var, devam.

kökandandtantdaddadodo

dugum = "dad".

kökandandtantdaddadodo

Kelime bitti; dugum.kelimeSonu = true → true döner (kelime bulundu).

public boolean ara(String kelime) {
  TrieDugumu dugum = kok;
  for (char ch : kelime.toCharArray()) {
    int i = ch - 'a';
    if (dugum.cocuk[i] == null)
      return false;
    dugum = dugum.cocuk[i];
  }
  return dugum.kelimeSonu;
}

Arama

Aramanın Üç Olası Sonucu

ara("dad") → trueTüm karakterler bulundu ve son düğüm işaretli.
kökandtdado
ara("da") → falseYol var, ama "da" düğümünde kelimeSonu = false: "da" yalnızca önek.
kökandtdado
ara("dog") → false"do" düğümünde cocuk[6] ('g') null: dal yok, erken çıkış.
kökandtdadog

Kesikli kırmızı çember: aranan ama trie'de bulunmayan düğüm.

Arama

Önek Sorgusu: onekVarMi (startsWith)

public boolean onekVarMi(String onek) {
  TrieDugumu dugum = kok;
  for (char ch : onek.toCharArray()) {
    int i = ch - 'a';
    if (dugum.cocuk[i] == null)
      return false;
    dugum = dugum.cocuk[i];
  }
  return true;  // ara: dugum.kelimeSonu
}
SorguaraonekVarMi
"dad"truetrue
"da"falsetrue
"an"falsetrue
"dog"falsefalse
Tek fark son satırdadır: önek sorgusu kelime sonunu denetlemez; yolun var olması yeterlidir. Trie: and, ant, dad, do. Zaman: O(P), P = önek uzunluğu.

Arama · Kodu adım adım çalıştır

onekVarMi("da") ve onekVarMi("dog")

danoatddkök

Trie'de and, ant, dad, do var (halkalı düğüm = kelime sonu). Önce onekVarMi("da"): dugum köke konur.

danoatddkök

ch = 'd', i = 3: cocuk[3] dolu → dugum o çocuğa iner ("d").

danoatddkök

ch = 'a', i = 0: cocuk[0] dolu → dugum o çocuğa iner ("da").

danoatddkök

Önek bitti ve yol kesilmedi → true. "da" düğümü işaretsiz olduğu için ara("da") false verirdi; önek sorgusu kelime sonuna bakmaz.

danoatddkök

İkinci çağrı onekVarMi("dog"): dugum yeniden köke konur.

danoatddkök

ch = 'd', i = 3: cocuk[3] dolu → dugum o çocuğa iner ("d").

danoatddkök

ch = 'o', i = 14: cocuk[14] dolu → dugum o çocuğa iner ("do").

danoatddköknullg

ch = 'g', i = 6: "do" düğümünün cocuk[6]'sı null → false. "do" bir kelime olsa da "dog" ile başlayan hiçbir kelime yok.

public boolean onekVarMi(String onek) {
  TrieDugumu dugum = kok;
  for (char ch : onek.toCharArray()) {
    int i = ch - 'a';
    if (dugum.cocuk[i] == null)
      return false;
    dugum = dugum.cocuk[i];
  }
  return true;  // ara: dugum.kelimeSonu
}

Arama

Önek Sayacı: onekSayisi O(P)

void ekle(String kelime) {
  TrieDugumu d = kok;
  d.gecen++;
  for (char ch : kelime.toCharArray()) {
    int i = ch - 'a';
    if (d.cocuk[i] == null) d.cocuk[i] = new TrieDugumu();
    d = d.cocuk[i];
    d.gecen++;          // bir kelime daha geçti
  }
  d.kelimeSonu = true;
}
int onekSayisi(String onek) {
  TrieDugumu d = kok;
  for (char ch : onek.toCharArray()) {
    d = d.cocuk[ch - 'a'];
    if (d == null) return 0;
  }
  return d.gecen;
}
addtndao11221112kök
Yeşil sayılar: gecen (kökte 4).
Sorgusonuç
onekSayisi("an")2
onekSayisi("da")1
onekSayisi("x")0
TrieDugumu'na int gecen alanı eklenir. Sayaçsız aynı soru alt ağacı gezmeyi (O(P + S · M)) gerektirirdi; bedeli düğüm başına bir int. Silmede yol boyunca sayaçlar azaltılır.

Arama

Joker Karakterli Arama: "d.d"

boolean joker(TrieDugumu d, String s, int j) {
  if (d == null) return false;
  if (j == s.length()) return d.kelimeSonu;
  char ch = s.charAt(j);
  if (ch != '.')
    return joker(d.cocuk[ch - 'a'], s, j + 1);
  for (int i = 0; i < 26; i++)
    if (joker(d.cocuk[i], s, j + 1)) return true;
  return false;
}
Desensonuçeşleşen
"d.d"truedad
".n."trueand
".o"truedo
"b.."falseb dalı yok
"...."false4 harfli yok
  • Trie: and, ant, dad, do. Çağrı joker(kok, desen, 0).
  • Normal harf aramayı tek yola indirir; '.' her dolu dalı dener (geri izleme). İlk eşleşmede true yukarı taşınır.
  • En kötü durum k joker için 26k dal, ama boş dallar hemen döner: gerçek maliyet trie'de var olan yollarla sınırlıdır. Hash tablosu bu sorguyu ancak tüm kelimeleri tarayarak yanıtlar.

Arama

Yazım Denetimi: Trie Üzerinde Düzenleme Uzaklığı

  • Soru: sözlükte "dod" kelimesine düzenleme uzaklığı (ekle / sil / değiştir) en çok 1 olan kelimeler hangileri?
  • Her kelimeyle ayrı ayrı uzaklık hesaplamak yerine trie derinlik öncelikli gezilir; her düğüm, ebeveyninin DP satırından tek bir satır üretir.
  • Ortak önekin satırı bir kez hesaplanır: "d" satırı hem dad hem do için kullanılır.
  • Budama: satırın en küçüğü k'yı aşarsa alt ağaçtaki hiçbir kelime k içinde olamaz; "an" satırının en küçüğü 2 > 1, and ve ant'a hiç inilmez.
düğüm"" d o dsonkarar
a1 1 2 3—devam
an2 2 2 3—buda (min 2)
d1 0 1 2—devam
da2 1 1 2—devam
dad3 2 2 11öner
do2 1 0 11öner

Satırın son hücresi, düğümün kelimesi ile "dod" arasındaki uzaklıktır.

Öneriler: dad (o → a) ve do (son d silinir). Satır başına O(|sorgu|) iş; budama sayesinde sözlüğün çoğu hiç ziyaret edilmez. Aynı gezinti klavyede yan yana harfleri ucuz sayan ağırlıklı uzaklıkla da çalışır; k büyüdükçe budama zayıflar ve ziyaret edilen düğüm sayısı hızla artar.

Arama · Kodu adım adım çalıştır

Otomatik Tamamlama: tamamla("te")

kökaaiininninnteateadtedntenoto

Önce dugumBul("te") kökten t → e inerek "te" düğümünü bulur (önek sorgusu gibi).

kökaaiininninnteateadtedntenoto

"te" düğümünden başlayarak alt ağaç derinlik öncelikli gezilir.

kökaaiininninnteateadtedntenoto

"te" bir kelime değil (kelimeSonu = false) → listeye eklenmez.

kökaaiininninnteateadtedntenoto

i = 0: cocuk[0] ('a') dolu → yol = "tea".

kökaaiininninnteateadtedntenoto

Özyinelemeli çağrı: topla("tea").

kökaaiininninnteateadtedntenoto

"tea" işaretli → sonuc'a eklenir. Çocuğu yok; döngü 26 indisi boş geçer.

kökaaiininninnteateadtedntenoto

Çağrı döndü; son harf silinir (yol = "te") → geri izleme (backtracking).

kökaaiininninnteateadtedntenoto

i = 3: cocuk[3] ('d') dolu → yol = "ted".

kökaaiininninnteateadtedntenoto

Özyinelemeli çağrı: topla("ted").

kökaaiininninnteateadtedntenoto

"ted" işaretli → sonuc'a eklenir. Çocuğu yok; döngü 26 indisi boş geçer.

kökaaiininninnteateadtedntenoto

Çağrı döndü; son harf silinir (yol = "te") → geri izleme (backtracking).

kökaaiininninnteateadtedntenoto

i = 13: cocuk[13] ('n') dolu → yol = "ten".

kökaaiininninnteateadtedntenoto

Özyinelemeli çağrı: topla("ten").

kökaaiininninnteateadtedntenoto

"ten" işaretli → sonuc'a eklenir. Çocuğu yok; döngü 26 indisi boş geçer.

kökaaiininninnteateadtedntenoto

Çağrı döndü; son harf silinir (yol = "te") → geri izleme (backtracking).

kökaaiininninnteateadtedntenoto

Döngü bitti; tamamla("te") → [tea, ted, ten]. İndis sırası (a < d < n) sayesinde sonuçlar alfabetik sıradadır.

List<String> tamamla(String onek) {
  List<String> sonuc = new ArrayList<>();
  TrieDugumu d = dugumBul(onek);
  if (d != null)
    topla(d, new StringBuilder(onek), sonuc);
  return sonuc;
}
void topla(TrieDugumu d, StringBuilder yol,
           List<String> sonuc) {
  if (d.kelimeSonu) sonuc.add(yol.toString());
  for (int i = 0; i < 26; i++)
    if (d.cocuk[i] != null) {
      yol.append((char) ('a' + i));
      topla(d.cocuk[i], yol, sonuc);
      yol.setLength(yol.length() - 1);
    }
}

Arama · Kodu adım adım çalıştır

enUzunOrtakOnek(): Kod İzleme

kartonkökkkakarkartkartokarton

Trie: kar, kart, karton. Kökten tek çocuklu ve işaretsiz düğümler boyunca inilir.

kartonkökkkakarkartkartokarton

Kök işaretsiz; çocuk sayılır: yalnız 'k' dolu → sayi = 1, tek = 10.

kartonkökkkakarkartkartokarton

'k' eklenir, d = "k". Bu düğümde de tek çocuk ('a', tek = 0) var.

kartonkökkkakarkartkartokarton

'a' eklenir, d = "ka"; tek çocuk 'r' (tek = 17).

kartonkökkkakarkartkartokarton

'r' eklenir, d = "kar". "kar" bir kelime → döngü biter: tek çocuğu olsa da "kar" kelimesi daha uzun öneki paylaşmaz.

kartonkökkkakarkartkartokarton

"kar" döner. Trie'de yalnız kart ve karton olsaydı inişe "kart"a kadar devam edilir, orada kelimeSonu durdururdu: "kart".

String enUzunOrtakOnek() {
  StringBuilder sb = new StringBuilder();
  TrieDugumu d = kok;
  while (!d.kelimeSonu) {
    int tek = -1, sayi = 0;
    for (int i = 0; i < 26; i++)
      if (d.cocuk[i] != null) { sayi++; tek = i; }
    if (sayi != 1) break;  // dal ya da yaprak
    sb.append((char) ('a' + tek));
    d = d.cocuk[tek];
  }
  return sb.toString();
}

Arama

Deneyin: Trie Üzerinde İşlemler

Yalnızca a–z küçük harfler (en fazla 10 harf, 14 kelime). Çift çember: kelime sonu · turuncu: son ziyaret edilen düğüm · kesikli kırmızı: bulunamayan dal. Enter = ekle.

Silme

Silme (Delete): Üç Durum

1. Kelime başka bir kelimenin önekiSon düğümün çocuğu vardır → yalnızca kelimeSonu = false yapılır, düğüm silinmez.
andkökan: işaret kalkar
2. Kelimenin kendine ait bir kuyruğu varSondan başa doğru, çocuğu kalmayan ve işaretsiz düğümler silinir; paylaşılan önekte durulur.
andköksilinir
3. Kelime trie'de yokDal bulunamaz ya da son düğüm işaretsizdir → hiçbir şey değişmez.
dokökg yok
Soru: Trie'de "apple" ve "app" varsa, "app" silindiğinde yapı nasıl değişir?
Cevabı göster

Yalnızca son 'p' düğümünün kelimeSonu işareti kaldırılır. Düğüm silinemez, çünkü "apple" onun altından devam eder (durum 1).

Silme · Adım adım

Silme Adım Adım: app, apple, apt

kökappappleappletapt

Başlangıç: app, apple, apt (kök hariç 6 düğüm). Aşağıdaki iki senaryo bu trie'den başlar.

kökappappleappletapt

sil("app"): a → p → p yolu izlenir; "app" düğümü işaretli, yani kelime var.

kökappleappletapt

kelimeSonu = false. Düğümün çocuğu ('l') olduğu için silinmez → durum 1. "apple" hâlâ bulunur.

kökappappleappletapt

sil("apple") (yeniden ilk trie'den): yol a-p-p-l-e izlenir; "apple" bulundu.

kökappappletapt

'e' düğümünün işareti kalkar; çocuğu yok → ebeveyn onu siler (cocuk[4] = null).

kökappappltapt

'l' düğümü artık çocuksuz ve işaretsiz → o da silinir.

kökappapptapt

"app" düğümü işaretli (başka bir kelime) → silme burada durur. Sonuç: app, apt; kök hariç 4 düğüm (durum 2).

Silme

Silme: Özyinelemeli Kod

// true: ebeveyn d'yi silebilir
boolean sil(TrieDugumu d, String k, int j) {
  if (j == k.length()) {        // kelime bitti
    if (!d.kelimeSonu) return false;
    d.kelimeSonu = false;
    return bosMu(d);            // çocuksuz mu?
  }
  int i = k.charAt(j) - 'a';
  TrieDugumu c = d.cocuk[i];
  if (c == null) return false;  // kelime yok
  if (sil(c, k, j + 1)) {
    d.cocuk[i] = null;          // çocuğu sil
    return !d.kelimeSonu && bosMu(d);
  }
  return false;
}
  • Çağrı: sil(kok, kelime, 0); kökün dönüş değeri yok sayılır.
  • Önce kelimenin sonuna inilir, silme kararları dönüşte (aşağıdan yukarı) verilir.
  • Düğüm, ancak çocuğu yoksa ve işaretsizse silinir.
  • bosMu(d): 26 çocuğun hepsi null mı? (O(M)).
  • Zaman: O(L · M) dizi ile; çocuk sayacı tutulursa O(L).

Silme · Kodu adım adım çalıştır

sil(kok, "dad", 0)

danoatddkök

sil(kök, "dad", 0): kelimenin ilk karakteri 'd' → i = 3, c = kökün 'd' çocuğu. Silme kararları dönüşte verilecek; önce aşağı inilir.

danoatddkök

sil(d, "dad", 1): 'a' → i = 0, c = "da" düğümü. Özyinelemeli çağrı bir düzey daha iner.

danoatddkök

sil(da, "dad", 2): 'd' → i = 3, c = "dad" düğümü.

danoatddkök

j = 3 = k.length(): kelime bitti. "dad" işaretli olduğu için kelimeSonu = false yapılır.

danoatddkök

bosMu(dad) = true: düğümün hiç çocuğu yok → true döner; ebeveyn bu düğümü silebilir.

danoatdköksilindi

sil(da) true döndü → d.cocuk[3] = null: "dad" düğümü koparıldı.

danoatdkök

"da" işaretsiz ve artık çocuksuz → true: bu düğüm de silinebilir.

danotdköksilindi

sil(d) true döndü → d.cocuk[0] = null: "da" düğümü koparıldı.

danotdkök

"d" işaretsiz ama "do" için 'o' çocuğu var → bosMu false → false. Silme burada durur.

danotdkök

Kökte sil(d) false döndü → kök bir şey silmez, false döner (dönüş değeri yok sayılır). Kalan kelimeler: and, ant, do.

// true: ebeveyn d'yi silebilir
boolean sil(TrieDugumu d, String k,
            int j) {
  if (j == k.length()) {  // bitti
    if (!d.kelimeSonu) return false;
    d.kelimeSonu = false;
    return bosMu(d);   // çocuksuz?
  }
  int i = k.charAt(j) - 'a';
  TrieDugumu c = d.cocuk[i];
  if (c == null) return false;
  if (sil(c, k, j + 1)) {
    d.cocuk[i] = null;  // çocuğu sil
    return !d.kelimeSonu && bosMu(d);
  }
  return false;
}

Analiz

Karmaşıklık

İşlemZamanAçıklama
ekle(kelime)O(L)Her karakter için bir adım; en fazla L yeni düğüm
ara(kelime)O(L)Dal yoksa daha erken biter
onekVarMi(onek)O(P)P: önek uzunluğu
tamamla(onek)O(P + S · M)S: önek düğümünün alt ağacındaki düğüm sayısı
sil(kelime)O(L · M)bosMu 26 hücreyi tarar; çocuk sayacıyla O(L)
Bellek: O(N · M)N: düğüm sayısı, M: alfabe boyutu (26). N ≤ (tüm kelimelerin toplam uzunluğu) + 1.
n'den bağımsızSüreler saklanan kelime sayısına bağlı değildir; BST'deki log n çarpanı yoktur.

Analiz

Bellek: Sayısal Örnek

Trie: and, ant, dad, do

Toplam karakter3 + 3 + 3 + 2 = 11
Düğüm sayısı N1 (kök) + 8 = 9
Çocuk referansı9 × 26 = 234
Dolu referans8 (kenar sayısı)
Boş (null) referans226 (≈ %97)
kökandandtantdaddadodo
Ortak önekler 11 karakteri 8 düğüme indirir, ama 26 öğeli diziler belleğin büyük kısmını null ile doldurur. Alfabe büyüdükçe (Unicode: 65 536+) sorun büyür.

Analiz

Avantajlar ve Dezavantajlar

Avantajlar

  • Hızlı önek (prefix) tabanlı arama: otomatik tamamlama.
  • Kelime arama O(L) zaman (L: kelime uzunluğu).
  • Çakışan önekler tek kez saklanır: bellek paylaşımı.
  • Gezinme kelimeleri sözlük sırasında verir; hash çakışması yoktur.

Dezavantajlar

  • Yüksek bellek tüketimi, özellikle büyük alfabelerde.
  • Alan karmaşıklığı O(N · M) (N: düğüm sayısı, M: alfabe boyutu).
  • Ortak öneki az olan kümelerde (ör. rastgele anahtarlar) kazanç yoktur.
  • Çok sayıda küçük nesne: önbellek (cache) dostu değildir.

Analiz

Çocukları Saklamanın Yolları

GösterimÇocuğa erişimDüğüm başına bellekNot
TrieDugumu[26] diziO(1)M referansBasit, hızlı; seyrek düğümlerde israf
HashMap<Character, TrieDugumu>O(1) ort.çocuk sayısı kadarHer alfabe (Türkçe, Unicode) çalışır; sıra korunmaz
TreeMap / sıralı listeO(log M)çocuk sayısı kadarAlfabetik gezinme korunur
Üçlü arama trie'si (TST)O(log M) ort.3 referansHer düğümde küçük / eşit / büyük bağlantısı
Trie'nin mantığı değişmez; yalnızca dugum.cocuk[i] yerine dugum.cocuk.get(ch) gibi bir çağrı gelir. Seçim: alfabe boyutu, bellek ve sıralı çıktı ihtiyacına göre yapılır.
dizi: 26 referansa ve d dolu, 24 hücre boşaHashMap: yalnız 2 giriş'a' → düğüm'd' → düğüm

Analiz

Trie mi, Hash Tablosu mu, BST mi?

İşlem (n kelime, uzunluk L)TrieHash tablosuDengeli BST
Tam kelime aramaO(L)O(L) ortalamaO(L · log n)
Önek sorgusuO(P)desteklenmezO(P · log n)
Önekle başlayan k kelimeO(P + S · M)tüm tabloyu taraO(P · log n + k · L)
Sıralı listelemeDFS, O(N · M)sıralama gerekirkök ortada, O(n)
En kötü durumO(L)O(n · L) (çakışma)O(L · log n)
Bellekyüksekdüşükdüşük

Hash tablosu da kelimenin hash değerini hesaplamak için L karakterin tümünü okur; bu yüzden tam aramada ikisi aynı mertebededir. Trie'nin farkı önek ve sıra sorgularındadır. S: önek düğümünün alt ağacındaki düğüm sayısı.

Analiz

Trie ile Sözlük Sırası: Dizgileri Sıralamak

  • Girdi sırası: dog, ant, do, and, dad. Hepsi trie'ye eklenir.
  • Kökten önce-kök (preorder) gezilir; çocuklar 'a' → 'z' sırasıyla ziyaret edilir, işaretli düğümde kelime yazılır.
  • Çıktı: and, ant, dad, do, dog. Düğüm, çocuklarından önce yazıldığı için önek, uzantısından önce gelir (do < dog).
  • Toplam karakter sayısı N ve sabit alfabe için ekleme O(N), gezinme O(N·M) (M = 26 çocuk denetimi): karşılaştırmalı sıralamanın O(n log n) karşılaştırmasına ve her karşılaştırmanın O(L) maliyetine gerek kalmaz.
  • Aynı fikir “burst sort” ve MSD basamak sıralamasının dizgi sürümlerinin temelidir.
addtngdao12354kök
Yeşil: önce-kök gezinmede yazılma sırası.

Uygulamalar ve Türevler

Kullanım Alanları

Otomatik tamamlamaArama kutusu, IDE: yazılan önek düğümüne inilir, alt ağaçtaki kelimeler (sık kullanılanlar önce) önerilir.
Yazım denetimiSözlük trie'de tutulur; kelime yoksa trie üzerinde küçük düzenlemelerle (ekle / sil / değiştir) yakın kelimeler aranır.
IP yönlendirmeİkili trie (alfabe {0, 1}) ile en uzun önek eşleşmesi: 0110… adresi, 0* / 01* / 011* yollarından en uzununa, 011*'e gider.
Kelime oyunları ve T9Boggle / kelime bulmaca: yol bir önek değilse arama hemen kesilir (budama). Tuş dizisinden kelime önerme.
En uzun önek eşleşmesi: rotalar 0* → A, 01* → B, 011* → C. Adres 0110… için kökten 0 → 1 → 1 inilir; '0' dalı yoktur. Yol boyunca görülen son rota C'dir → paket C'ye gider.
kök0A1B1C0

Uygulamalar ve Türevler · Kodu adım adım çalıştır

enUzunEslesme(): İkili Trie ile Yönlendirme

011110kökABCDErotalar0* → A01* → B011* → C1* → D110* → E

İkili trie (alfabe {0, 1}); bazı düğümler bir rota taşır. adres = "0110": d = kök, enIyi = null.

011110kökABCDErotalar0* → A01* → B011* → C1* → D110* → E

k = 0: kökte rota yok. Bit '0' → d = "0".

011110kökABCDErotalar0* → A01* → B011* → C1* → D110* → E

k = 1: "0" düğümünde A var → enIyi = A. Bit '1' → d = "01".

011110kökABCDErotalar0* → A01* → B011* → C1* → D110* → E

k = 2: "01"de B → enIyi = B (daha uzun önek kazanır). d = "011".

011110kökABCDE0 yokrotalar0* → A01* → B011* → C1* → D110* → E

k = 3: "011"de C → enIyi = C. Bit '0' dalı yok → d = null, C döner.

011110kökABCDErotalar0* → A01* → B011* → C1* → D110* → E

Yeni adres "1001": k = 0, kökte rota yok; d = "1".

011110kökABCDE0 yokrotalar0* → A01* → B011* → C1* → D110* → E

k = 1: "1"de D → enIyi = D. '0' dalı yok → D döner. Özel rota yoksa genel olan kullanılır.

011110kökABCDErotalar0* → A01* → B011* → C1* → D110* → E

"1101": iniş 1 → 11 → 110. "11" ara düğümdür (rotası yok), enIyi D olarak kalır.

011110kökABCDE1 yokrotalar0* → A01* → B011* → C1* → D110* → E

k = 3: "110"da E → enIyi = E; '1' dalı yok → E. Adım sayısı adres uzunluğuyla sınırlı.

String enUzunEslesme(String adres) {
  IkiliDugum d = kok;
  String enIyi = null;
  for (int k = 0; k < adres.length(); k++) {
    if (d.rota != null) enIyi = d.rota;  // daha uzun önek
    d = d.cocuk[adres.charAt(k) - '0'];
    if (d == null) return enIyi;
  }
  return d.rota != null ? d.rota : enIyi;
}

Uygulamalar ve Türevler

İkili Trie ile En Büyük XOR Çifti

  • Soru: [3, 10, 5, 25, 2, 8] içinde a ⊕ b'yi en büyük yapan çift? Kaba kuvvet n² çift dener.
  • Sayılar, en anlamlı bitten başlayarak 5 bitlik ikili trie'ye eklenir.
  • Her x için kökten inilirken her bitte ters bit dalı seçilir (varsa): o bit sonuçta 1 olur. Üst bitteki 1, alttaki bütün bitlerin toplamından değerlidir; bu yüzden açgözlü seçim doğrudur.
  • x = 5 = 00101 için istenen 11010: 1 ✓ (25), 1 ✓, 0 ✓, 1 yok → 0, 0 yok → 1: varılan 11001 = 25, 5 ⊕ 25 = 28.
sayıikili (5 bit)300011100101050010125110012000108010005 ⊕ 2511100 = 28
Yöntemzamann = 10⁵, B = 30
Tüm çiftlerO(n²)≈ 5·10⁹ işlem
İkili trieO(n·B)≈ 6·10⁶ adım

Uygulamalar ve Türevler

Üçlü Arama Trie'si (Ternary Search Trie)

  • Her düğüm bir karakter ve üç bağlantı taşır: sol (küçük), orta (eşit, sonraki karaktere geç), sag (büyük).
  • Bir düğümün sol/sağ zinciri, trie'deki “kardeş” çocuklar için küçük bir ikili arama ağacıdır.
  • Kelime sonu düğümde işaretlenir (halka). Ekleme sırası şekli değiştirir: önce eklenen kelime “omurga” olur.
kar, kal, at, ve, kasdüğümbağlantı
26 öğeli dizili trie1010 × 26 = 260
TST99 × 3 = 27
<===<>>=katarlsve
Düz çizgi: orta (=); kesikli: sol (<) ve sağ (>).
Arama L karakter için L orta adım + her düzeyde kardeşler arasında ikili arama: ortalama O(L + log n). Alfabe büyükken (Unicode) dizi yerine TST bellek ve hız dengesini korur (Bentley ve Sedgewick, 1997).

Uygulamalar ve Türevler · Kodu adım adım çalıştır

Üçlü Arama Trie'sinde ara("kas") ve ara("al")

<===<>>=katarlsve

TST: kar, kal, at, ve, kas. ara("kas"): d = kök ('k'), i = 0.

<===<>>=katarlsve

ch = 'k' = d.c ve son karakter değil → orta bağlantı, i = 1. d = 'a'.

<===<>>=katarlsve

ch = 'a' eşit → orta, i = 2. d = 'r'.

<===<>>=katarlsve

ch = 's' > 'r' → sağ bağlantı; i değişmez (aynı karakter, kardeş aranıyor). d = 's'.

<===<>>=katarlsve

ch = 's' eşit ve i = 2 son indis → d.son döner: true. 3 karakter için 4 düğüm ziyaret edildi.

<===<>>=katarlsve

ara("al"): ch = 'a' < 'k' → sol, d = 'a' (at kelimesinin dalı).

<===<>>=katarlsve

'a' eşit, son karakter değil → orta, i = 1, d = 't'.

<===<>>=katarlsvenull

ch = 'l' < 't' → sol; 't'nin sol bağlantısı null → döngü biter, false.

boolean ara(String s) {
  TstDugum d = kok;
  int i = 0;
  while (d != null) {
    char ch = s.charAt(i);
    if (ch < d.c) d = d.sol;
    else if (ch > d.c) d = d.sag;
    else if (i == s.length() - 1) return d.son;
    else { d = d.orta; i++; }
  }
  return false;
}

Uygulamalar ve Türevler

Sıkıştırılmış Trie (Radix / Patricia)

Standart trie: 8 düğüm
kökandandtantdaddadodo
Sıkıştırılmış trie: 6 düğüm
anddtadokökandantdaddo
  • Tek çocuklu ve işaretsiz düğüm zincirleri tek bir kenarda birleştirilir; kenar artık bir alt dizgi taşır ("an", "ad").
  • Her iç düğümün en az 2 çocuğu (ya da kelime sonu işareti) olduğundan düğüm sayısı O(n) olur (n: kelime sayısı).
  • Patricia (Morrison, 1968) bu fikrin ikili alfabe sürümüdür; IP yönlendirme tablolarında kullanılır.

Uygulamalar ve Türevler

Sonek Trie'si (Suffix Trie)

T = "abab" metninin tüm sonekleri bir trie'ye eklenir:

abab · bab · ab · b

  • P, T'nin bir alt dizgisi ⇔ P, bir sonekin öneki ⇔ P kökten başlayan bir yol.
  • Sorgu: onekVarMi(P) → O(m) (m = |P|), metnin uzunluğundan bağımsız. Örn. "ba" var, "bb" yok.
  • Bedel: n uzunluklu metin için O(n²) düğüm olabilir.
  • Sonek ağacı (suffix tree) = sıkıştırılmış sonek trie'si: O(n) düğüm, Ukkonen algoritmasıyla O(n) zamanda kurulur.
kökabababababbbabbab

Uygulamalar ve Türevler · Kodu adım adım çalıştır

sonekTrie("abab")

kökta0b1a2b3

t = "abab": 4 sonek (abab, bab, ab, b) sırayla eklenecek. Trie yalnızca kökten oluşuyor.

akökta0b1a2b3

s = 0: sonek "abab" eklenmeye başlar; d = kök. s = 0, j = 0: 'a' dalı yok → yeni düğüm, d oraya iner ("a").

abkökta0b1a2b3

s = 0, j = 1: 'b' dalı yok → yeni düğüm, d oraya iner ("ab").

abakökta0b1a2b3

s = 0, j = 2: 'a' dalı yok → yeni düğüm, d oraya iner ("aba").

ababkökta0b1a2b3

s = 0, j = 3: 'b' dalı yok → yeni düğüm, d oraya iner ("abab").

ababkökta0b1a2b3

Sonek "abab" bitti → son düğüm işaretlenir (halka).

abbabkökta0b1a2b3

s = 1: sonek "bab" eklenmeye başlar; d = kök. s = 1, j = 1: 'b' dalı yok → yeni düğüm, d oraya iner ("b").

abbaabkökta0b1a2b3

s = 1, j = 2: 'a' dalı yok → yeni düğüm, d oraya iner ("ba").

abbaabbkökta0b1a2b3

s = 1, j = 3: 'b' dalı yok → yeni düğüm, d oraya iner ("bab").

abbaabbkökta0b1a2b3

Sonek "bab" bitti → son düğüm işaretlenir (halka).

abbaabbkökta0b1a2b3

s = 2: "ab" yolu abab eklenirken zaten oluşmuştu; yeni düğüm açılmadan inilir ve "ab" işaretlenir.

abbaabbkökta0b1a2b3

s = 3: "b" yolu da var; yalnızca işaret eklenir. Ortak önekler bir kez saklandı: kök dahil 8 düğüm.

abbaabbkökta0b1a2b3

Sonek trie'si hazır. "ba" kökten bir yol → alt dizgi; "bb" yolu yok → alt dizgi değil. Sorgu O(m).

TrieDugumu sonekTrie(String t) {
  TrieDugumu kok = new TrieDugumu();
  for (int s = 0; s < t.length(); s++) {
    TrieDugumu d = kok;
    for (int j = s; j < t.length(); j++) {
      int i = t.charAt(j) - 'a';
      if (d.cocuk[i] == null)
        d.cocuk[i] = new TrieDugumu();
      d = d.cocuk[i];
    }
    d.kelimeSonu = true;  // sonek bitti
  }
  return kok;
}

Uygulamalar ve Türevler

Aho–Corasick: Birçok Örüntüyü Tek Geçişte Aramak

  • Örüntüler (he, she, his, hers) bir trie'ye eklenir; metin bir kez soldan sağa okunur.
  • Hata bağlantısı (kesikli): düğümün dizgisinin trie'de bulunan en uzun özel soneki. "she" → "he", "sh" → "h", "hers" → "s".
  • Dal yoksa hata bağlantısıyla geri çekilinir; KMP önek tablosunun çok örüntülü karşılığıdır.
  • Çıktı kümesi hata zinciri boyunca birleşir: "she" düğümü he'yi de bildirir.
Metin "ushers"durumbulunan
u, s, hkök, s, sh—
e (3. indis)sheshe, he
rshe → he → her—
s (5. indis)hershers
hersisshe{he}{hers}{his}{she, he}

Zaman O(n + m + z): n metin, m örüntü uzunlukları toplamı, z eşleşme sayısı (Aho ve Corasick, 1975). Virüs imzası tarama ve yasaklı kelime süzgeçlerinde kullanılır.

Uygulamalar ve Türevler

Trie Ailesi: Hangi Soruya Hangi Yapı?

YapıSakladığıGüçlü olduğu soruSorgu
Standart triekelime kümesitam kelime, önek, otomatik tamamlamaO(L)
Önek sayaçlı trie+ düğümden geçen kelime sayısı“bu önekle kaç kelime var?”O(P)
Sıkıştırılmış trietek çocuklu zincirler birleşikuzun ortak önekli anahtarlar, bellekO(L)
Üçlü arama trie'sidüğüm başına 3 bağlantıbüyük alfabe, az bellekO(L + log n) ort.
İkili triebit dizileri (IP, tam sayı)en uzun önek eşleşmesi, en büyük XORO(B)
Sonek trie'si / dizisitüm sonekler (dizi: O(n) bellek)“P, T'nin alt dizgisi mi?”O(m) / O(m log n)
Aho–Corasickörüntü trie'si + hata bağlantılarıbirçok örüntüyü metinde aynı anda bulmakO(n + z)
Ortak fikir hep aynıdır: anahtarı karakter (ya da bit) bit ilerleyerek okumak ve ortak önekleri paylaşmak. Farklar, düğümde neyin saklandığında ve çocuklara nasıl erişildiğindedir. Seçimde önce sorunun türü (tam eşleşme, önek, alt dizgi, çok örüntü), sonra alfabe büyüklüğü ve bellek bütçesi belirleyicidir.

Özet

Özet

KonuHatırlanacaklar
Yapıboş kök; her kenar bir karakter; kelime = kökten yol; ortak önekler paylaşılır
DüğümTrieDugumu[26] cocuk + boolean kelimeSonu; indis = ch − 'a'
Eklemeyoksa düğüm oluştur, varsa ilerle; sonda işaretle — O(L)
Aramadal yoksa false; sonda kelimeSonu döner — O(L)
Önekaynı yol, işaret denetimi yok — O(P); tamamla = önek düğümünden DFS
Silmeişareti kaldır; çocuksuz ve işaretsiz düğümleri aşağıdan yukarı sil
BellekO(N · M); seyrek düğümlerde HashMap / TST / sıkıştırılmış trie
Türevlerradix/Patricia trie, sonek trie'si → sonek ağacı, sonek dizisi

Özet

Etkileşimli Simülatörler

Trie ve Sonek YapılarıVirgülle ayrılmış kelimelerden trie kurulumu ve önek sorgusu; bir metnin sonek dizisi ile sonek trie'sinin karşılaştırması.
Önek Fonksiyonu ve Z AlgoritmasıAynı dizgi için doğrusal zamanda kurulan KMP önek (π) dizisi ve Z dizisi yan yana.

Özet

Sık Yapılan Hatalar

kelimeSonu'nu unutmakAramada yalnızca yolun varlığına bakmak her öneki kelime sayar: "da" bulunmuş gibi görünür.
ara ile onekVarMi'yi karıştırmakÖnek sorgusu son düğümün işaretine bakmaz; tam kelime araması bakmak zorundadır.
Alfabe dışı karakter'A' − 'a' = −32, 'ş' − 'a' = 254: dizi sınırı aşılır. Girdi küçük harfe çevrilmeli ya da HashMap kullanılmalıdır.
Silmede ortak düğümü silmekYolu baştan sona silmek, öneki paylaşan diğer kelimeleri de yok eder. Yalnızca çocuksuz ve işaretsiz düğümler silinir.
Karmaşıklığı n ile ifade etmekTrie işlemleri O(log n) ya da O(n) değil, O(L)'dir: kelime sayısından bağımsızdır.
Belleği hafife almakHer düğüm 26 referans taşır; milyonlarca düğümde yüzlerce MB. Seyrek trie'de HashMap ya da sıkıştırma gerekir.

Özet

Örnek Problemler

Kelimeler sırayla eklenir: car, cart, care, cat, dog (26 öğeli dizili trie).

  1. Trie'yi çizin. Kök dahil kaç düğüm vardır?
  2. ara("ca") ve onekVarMi("ca") ne döndürür?
  3. tamamla("car") sonucunu sırasıyla yazın.
  4. sil("car") ve (ilk trie'den) sil("cart") kaç düğüm siler?
  5. Toplam kaç çocuk referansı null'dır?
  6. Aynı beş kelimenin sıkıştırılmış trie'si kök dahil kaç düğümlüdür?
Cevapları göster
  1. c-a-r(•)-t(•), car-e(•), ca-t(•); d-o-g(•) → kök hariç 9, kök dahil 10 düğüm (17 karakter).
  2. ara("ca") = false ("ca" işaretsiz); onekVarMi("ca") = true.
  3. [car, care, cart] — DFS önce düğümün kendisini, sonra çocukları indis sırasıyla ('e' < 't') verir.
  4. sil("car"): 0 düğüm (yalnızca işaret kalkar); sil("cart"): 1 düğüm ('t'), "car" işaretli olduğu için durur.
  5. 10 × 26 = 260 referans, 9'u dolu (kenar sayısı) → 251 null.
  6. 7: kök, "ca", r(•), t(•) (cart), e(•) (care), t(•) (cat), "dog"(•); c–a ve d–o–g zincirleri tek düğüme iner.

Özet

Alıştırmalar

  1. int kelimeSayisi(TrieDugumu d): trie'deki kelime sayısını bulun.
  2. int onekSayisi(String onek): bu önekle başlayan kelime sayısını O(P)'de döndürün.
  3. String enUzunOrtakOnek(): trie'deki tüm kelimelerin en uzun ortak önekini bulun.
  4. Çocukları HashMap<Character, TrieDugumu> ile saklayan, Türkçe karakterleri destekleyen trie yazın.
  5. A, to, tea, ted, ten, i, in, inn anahtarlarının trie'sinde kök dahil kaç düğüm vardır?
  6. Sonek trie'si ile "abab" içinde "bab" ve "bb" alt dizgilerini arayın; kaç karşılaştırma yapılır?
Cevaplar / ipuçları
  1. DFS: (d.kelimeSonu ? 1 : 0) + tüm dolu çocukların kelimeSayisi toplamı. O(N · M).
  2. Düğüme int gecen sayacı eklenir; ekle her geçtiği düğümde sayacı artırır. Önek düğümünün sayacı = cevap.
  3. Kökten inilir: düğümün tek çocuğu varken ve kelimeSonu false iken ilerlenir; geçilen karakterler cevaptır.
  4. cocuk.get(ch) / cocuk.computeIfAbsent(ch, x -> new TrieDugumu()); indis hesabı kalkar.
  5. A, t, to, te, tea, ted, ten, i, in, inn = 10 düğüm + kök = 11.
  6. "bab": b → a → b yolu var → bulundu (3 adım). "bb": b'den sonra 'b' dalı yok → yok (2 adım).

Özet

Kaynaklar

  • Sedgewick ve Wayne, Algorithms, 4. baskı, 2011 (Bölüm 5.2: Tries).
  • Cormen, Leiserson, Rivest ve Stein, Introduction to Algorithms, 4. baskı, 2022.
  • E. Fredkin, "Trie Memory", Communications of the ACM 3(9), 1960.
  • D. R. Morrison, "PATRICIA — Practical Algorithm To Retrieve Information Coded in Alphanumeric", Journal of the ACM 15(4), 1968.
  • U. Manber ve G. Myers, "Suffix Arrays: A New Method for On-Line String Searches", SIAM Journal on Computing 22(5), 1993.
  • D. Gusfield, Algorithms on Strings, Trees, and Sequences, Cambridge University Press, 1997.

Son

Bölüm 5.3: Trie Veri Yapısı
Önceki bölüm: Bölüm 5.2: İleri Dizgi Algoritmaları · Sonraki bölüm: Bölüm 6: Böl ve Fethet Yaklaşımı · Terim sözlüğü
1 / 1 Sercan KÜLCÜ, Tüm hakları saklıdır.