rust_algorithms/tree/
trie.rs1use std::collections::HashMap;
4
5#[derive(Debug, Default)]
6struct Node {
7 children: HashMap<char, Node>,
8 is_word: bool,
10}
11
12#[derive(Debug, Default)]
60pub struct Trie {
61 root: Node,
62 len: usize,
63}
64
65impl Trie {
66 pub fn new() -> Self {
68 Self::default()
69 }
70
71 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 pub fn contains(&self, word: &str) -> bool {
87 self.node_for(word).is_some_and(|n| n.is_word)
88 }
89
90 pub fn starts_with(&self, prefix: &str) -> bool {
92 self.node_for(prefix).is_some()
93 }
94
95 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 pub fn words(&self) -> Vec<String> {
107 self.words_with_prefix("")
108 }
109
110 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 pub fn len(&self) -> usize {
129 self.len
130 }
131
132 pub fn is_empty(&self) -> bool {
134 self.len == 0
135 }
136
137 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 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")); 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}