Skip to main content

rust_algorithms/tree/
trie.rs

1//! Trie (prefiks daraxti) — so'zlar lug'ati va avtoto'ldirish uchun.
2
3use std::collections::HashMap;
4
5#[derive(Debug, Default)]
6struct Node {
7    children: HashMap<char, Node>,
8    /// Shu tugunda tugaydigan so'z bormi?
9    is_word: bool,
10}
11
12/// Trie — har bir qirra bitta harf, ildizdan tugungacha bo'lgan yo'l — prefiks.
13///
14/// ```text
15///        (root)
16///        /    \
17///      o        k
18///      |        |
19///      l        i
20///     / \       |
21///    m   t      t
22///    |   |      |
23///   [a] [i]    [o]      ← [ ] = so'z shu yerda tugaydi
24///        |
25///       [n]
26///  → "olma", "olti", "oltin", "kito"
27/// ```
28///
29/// | Amal | Murakkablik | Izoh |
30/// |---|---|---|
31/// | `insert` / `contains` | O(m) | m — so'z uzunligi, **lug'at hajmiga bog'liq emas!** |
32/// | `starts_with` | O(m) | prefiks bormi |
33/// | `words_with_prefix` | O(m + natija) | avtoto'ldirish |
34///
35/// **Qayerda ishlatiladi:** qidiruv qatoridagi avtoto'ldirish, telefondagi T9,
36/// imlo tekshirgich, IP marshrutlash jadvallari, so'z o'yinlari.
37///
38/// **HashSet dan farqi:** `HashSet` "bu so'z bormi?" ga javob beradi, Trie esa
39/// **prefiks** bo'yicha ham qidira oladi va xotirada umumiy prefikslarni bir marta saqlaydi.
40///
41/// # Misol
42/// ```
43/// use rust_algorithms::tree::Trie;
44///
45/// let mut t = Trie::new();
46/// for so_z in ["olma", "olti", "oltin", "kitob"] {
47///     t.insert(so_z);
48/// }
49///
50/// assert!(t.contains("olma"));
51/// assert!(!t.contains("ol"));        // "ol" — so'z emas, faqat prefiks
52/// assert!(t.starts_with("ol"));
53///
54/// let mut topilgan = t.words_with_prefix("olt");
55/// topilgan.sort();
56/// assert_eq!(topilgan, vec!["olti", "oltin"]);
57/// assert_eq!(t.len(), 4);
58/// ```
59#[derive(Debug, Default)]
60pub struct Trie {
61    root: Node,
62    len: usize,
63}
64
65impl Trie {
66    /// Bo'sh trie.
67    pub fn new() -> Self {
68        Self::default()
69    }
70
71    /// So'z qo'shadi. Yangi so'z bo'lsa `true`.
72    pub fn insert(&mut self, word: &str) -> bool {
73        let mut kursor = &mut self.root;
74        for ch in word.chars() {
75            kursor = kursor.children.entry(ch).or_default();
76        }
77        if kursor.is_word {
78            return false;
79        }
80        kursor.is_word = true;
81        self.len += 1;
82        true
83    }
84
85    /// Aynan shu so'z lug'atda bormi?
86    pub fn contains(&self, word: &str) -> bool {
87        self.node_for(word).is_some_and(|n| n.is_word)
88    }
89
90    /// Shu prefiks bilan boshlanadigan so'z bormi?
91    pub fn starts_with(&self, prefix: &str) -> bool {
92        self.node_for(prefix).is_some()
93    }
94
95    /// Prefiks bilan boshlanadigan barcha so'zlar (avtoto'ldirish).
96    pub fn words_with_prefix(&self, prefix: &str) -> Vec<String> {
97        let mut out = Vec::new();
98        if let Some(node) = self.node_for(prefix) {
99            let mut buf: Vec<char> = prefix.chars().collect();
100            Self::collect(node, &mut buf, &mut out);
101        }
102        out
103    }
104
105    /// Lug'atdagi barcha so'zlar.
106    pub fn words(&self) -> Vec<String> {
107        self.words_with_prefix("")
108    }
109
110    /// So'zni o'chiradi (tugunlar qoladi, faqat belgisi olib tashlanadi).
111    pub fn remove(&mut self, word: &str) -> bool {
112        let mut kursor = &mut self.root;
113        for ch in word.chars() {
114            match kursor.children.get_mut(&ch) {
115                Some(next) => kursor = next,
116                None => return false,
117            }
118        }
119        if !kursor.is_word {
120            return false;
121        }
122        kursor.is_word = false;
123        self.len -= 1;
124        true
125    }
126
127    /// So'zlar soni.
128    pub fn len(&self) -> usize {
129        self.len
130    }
131
132    /// Bo'shmi?
133    pub fn is_empty(&self) -> bool {
134        self.len == 0
135    }
136
137    /// Berilgan prefiksga mos tugunni topadi.
138    fn node_for(&self, prefix: &str) -> Option<&Node> {
139        let mut kursor = &self.root;
140        for ch in prefix.chars() {
141            kursor = kursor.children.get(&ch)?;
142        }
143        Some(kursor)
144    }
145
146    /// Tugundan boshlab barcha so'zlarni yig'adi (DFS).
147    fn collect(node: &Node, buf: &mut Vec<char>, out: &mut Vec<String>) {
148        if node.is_word {
149            out.push(buf.iter().collect());
150        }
151        for (ch, bola) in node.children.iter() {
152            buf.push(*ch);
153            Self::collect(bola, buf, out);
154            buf.pop();
155        }
156    }
157}
158
159impl<S: AsRef<str>> FromIterator<S> for Trie {
160    fn from_iter<I: IntoIterator<Item = S>>(iter: I) -> Self {
161        let mut t = Trie::new();
162        for s in iter {
163            t.insert(s.as_ref());
164        }
165        t
166    }
167}
168
169#[cfg(test)]
170mod tests {
171    use super::*;
172
173    fn lugat() -> Trie {
174        ["olma", "olti", "oltin", "kitob", "kit", "anor"]
175            .into_iter()
176            .collect()
177    }
178
179    #[test]
180    fn qoshish_va_qidirish() {
181        let t = lugat();
182        assert_eq!(t.len(), 6);
183        assert!(t.contains("kit"));
184        assert!(t.contains("kitob"));
185        assert!(!t.contains("kito"));
186        assert!(t.starts_with("kito"));
187        assert!(!t.starts_with("z"));
188    }
189
190    #[test]
191    fn takror_qoshilmaydi() {
192        let mut t = Trie::new();
193        assert!(t.insert("bir"));
194        assert!(!t.insert("bir"));
195        assert_eq!(t.len(), 1);
196    }
197
198    #[test]
199    fn prefiks_boyicha_royxat() {
200        let t = lugat();
201        let mut w = t.words_with_prefix("ol");
202        w.sort();
203        assert_eq!(w, vec!["olma", "olti", "oltin"]);
204
205        let mut hammasi = t.words();
206        hammasi.sort();
207        assert_eq!(
208            hammasi,
209            vec!["anor", "kit", "kitob", "olma", "olti", "oltin"]
210        );
211
212        assert!(t.words_with_prefix("zzz").is_empty());
213    }
214
215    #[test]
216    fn ochirish() {
217        let mut t = lugat();
218        assert!(t.remove("olti"));
219        assert!(!t.contains("olti"));
220        assert!(t.contains("oltin")); // uzunroq so'z saqlanib qoldi
221        assert!(!t.remove("olti"));
222        assert_eq!(t.len(), 5);
223    }
224
225    #[test]
226    fn bosh_satr_va_unicode() {
227        let mut t = Trie::new();
228        assert!(t.insert(""));
229        assert!(t.contains(""));
230        assert!(t.insert("o'zbek"));
231        assert!(t.insert("o'zbekiston"));
232        assert_eq!(t.words_with_prefix("o'zbeki"), vec!["o'zbekiston"]);
233    }
234}