İçindekiler
Bir arama kutusuna pro yazdığınızda saniyeler değil, milisaniyeler içinde programlama, proje ve profil önerilerinin belirmesi sihir değildir: sahnenin arkasında çoğu zaman Trie veri yapısı çalışır. Prefix tree olarak da bilinen Trie, kelimeleri karakter karakter dallandırarak saklar. Böylece tüm kelime listesini her tuş vuruşunda baştan sona dolaşmak yerine, yalnızca yazılan öneke karşılık gelen dalı takip ederiz.
``
Trie mantığı: Kelimeler bir ağacın dallarıdır
Trie içinde her düğüm bir karakteri temsil eder; kök düğüm ise boş başlangıç noktasıdır. cat, car ve card kelimelerini eklediğimizi düşünelim. İlk iki karakter olan c ve a, kelimeler arasında ortak olduğundan tek kez saklanır. Ardından t ve r için dallanma gerçekleşir. Bir düğümdeki isWord işareti, o noktaya kadar gelinen karakter dizisinin geçerli bir kelime olup olmadığını belirtir.
Bir kelimenin uzunluğu $L$ ise ekleme ve arama işlemlerinin zaman maliyeti genellikle $O(L)$ olur. Önemli nokta şudur: Bu maliyet, sözlükteki toplam kelime sayısı $N$ ile doğrudan büyümez. Elbette öneri listesini üretirken bulunan sonuç sayısı $K$ da maliyete eklenir: $O(P + K)$; burada $P$, kullanıcının yazdığı prefix uzunluğudur.
| Yaklaşım | Prefix arama maliyeti | Güçlü yanı | Zayıf yanı |
|---|---|---|---|
| Dizi + filtre | $O(N \times P)$ | Uygulaması çok kolay | Büyük sözlüklerde yavaşlar |
| Sıralı dizi + ikili arama | $O(\log N + K)$ | Bellek açısından verimli | Ekleme maliyetlidir |
| Trie | $O(P + K)$ | Anlık öneriler için idealdir | Düğüm sayısı bellek tüketebilir |
JavaScript ile çalışan bir Trie
Aşağıdaki sınıf, kelime ekler; girilen prefix için sınırlı sayıda öneri döndürür. Çocuk düğümlerini Map ile tutmak, karakter erişimini okunaklı ve ortalama durumda hızlı hale getirir.
class TrieNode {
constructor() {
this.children = new Map();
this.isWord = false;
}
}
class Trie {
constructor() {
this.root = new TrieNode();
}
insert(word) {
let node = this.root;
for (const char of word.toLocaleLowerCase('tr-TR')) {
if (!node.children.has(char)) {
node.children.set(char, new TrieNode());
}
node = node.children.get(char);
}
node.isWord = true;
}
suggest(prefix, limit = 5) {
const normalized = prefix.toLocaleLowerCase('tr-TR');
let node = this.root;
for (const char of normalized) {
node = node.children.get(char);
if (!node) return [];
}
const results = [];
const walk = (current, text) => {
if (results.length >= limit) return;
if (current.isWord) results.push(text);
for (const [char, child] of current.children) {
walk(child, text + char);
}
};
walk(node, normalized);
return results;
}
}
const trie = new Trie();
['program', 'programlama', 'proje', 'profil', 'python'].forEach(w => trie.insert(w));
console.log(trie.suggest('pro')); // ["program", "programlama", "proje", "profil"]
walk fonksiyonu, prefix düğümünden itibaren derinlik öncelikli dolaşım yapar. limit kontrolü kritik bir ayrıntıdır: Çok geniş bir dalda binlerce sonucu tek seferde arayüze taşımak hem gereksiz hem de kullanıcı deneyimi açısından yorucudur.
Arama kutusuna bağlamak
Gerçek bir arayüzde input olayını dinleyip her değişimde suggest çağırabilirsiniz. Boş sorguda öneri göstermemek ve kullanıcı hızlı yazarken aramayı küçük bir debounce ile geciktirmek iyi fikirdir. Ayrıca Türkçe karakterler için toLocaleLowerCase('tr-TR') kullanılması, özellikle I/ı ve İ/i dönüşümlerinde beklenmedik eşleşmeleri önler.
Trie temel sürümde sonuçları eklenme sırasına göre döndürür. Popülerlik tabanlı öneriler istiyorsanız düğümlere arama sayacı ekleyebilir, sonuçları bu puana göre sıralayabilirsiniz. Yazım toleransı, çok kelimeli arama ve kişiselleştirme eklendiğinde Trie tek başına yeterli olmayabilir; fakat hızlı prefix eşleştirme için hâlâ sağlam, öğretici ve etkileyici bir başlangıçtır.
Yorumlar