Skip to content

Commit ed9ef9b

Browse files
committed
Add trie.dart
1 parent c13d72e commit ed9ef9b

File tree

1 file changed

+159
-0
lines changed

1 file changed

+159
-0
lines changed

Diff for: lib/trie/trie.dart

+159
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
/// Trie is an ordered tree data structure used to store a dynamic set
2+
/// or associative array where the keys are usually strings.
3+
class Trie<V extends Comparable> {
4+
/// Root of the trie.
5+
TrieNode root;
6+
7+
/// Separates the value into it's components.
8+
final Function splitter;
9+
10+
List _components;
11+
12+
/// Initialises trie with custom set of values.
13+
Trie(Set components, this.splitter) {
14+
_components = [...components];
15+
}
16+
17+
/// Generates trie of lower-case alphabets.
18+
Trie.ofAlphabets()
19+
: this({...List.generate(26, (i) => String.fromCharCode(97 + i))},
20+
(value) => value.split(''));
21+
22+
/// Returns the set of [components] that make up a value.
23+
Set<V> get components => {..._components};
24+
25+
/// Tests if this trie is empty.
26+
bool get isEmpty => root?.children == null ? true : false;
27+
28+
/// Adds a [value] to the trie.
29+
void add(V value) {
30+
var list = _split(value);
31+
if (isEmpty) {
32+
root ??= TrieNode({..._components});
33+
}
34+
_add(root, list);
35+
}
36+
37+
/// Checks if [value] is contained in the trie.
38+
bool contains(V value) {
39+
var list = _split(value);
40+
return isEmpty ? false : _contains(root, list);
41+
}
42+
43+
/// Deletes [value] from the trie.
44+
void delete(V value) {
45+
var list = _split(value);
46+
var returnValue = _delete(root, list);
47+
returnValue ?? nullify();
48+
}
49+
50+
/// Empty the trie.
51+
void nullify() => root?.children = null;
52+
53+
/// Traverses the path following [value]
54+
/// and marks `node.isValue` to true at end.
55+
void _add(TrieNode node, List<V> value) {
56+
if (value.isEmpty) {
57+
node.isValue = true;
58+
return;
59+
}
60+
var path = _indexOf(value.first);
61+
value = value.sublist(1);
62+
63+
if (node.children[path] == null) {
64+
node.children[path] = TrieNode(components);
65+
}
66+
67+
_add(node.children[path], value);
68+
}
69+
70+
bool _contains(TrieNode node, List<V> value) {
71+
if (value.isEmpty) {
72+
return node.isValue;
73+
}
74+
var path = _indexOf(value.first);
75+
value = value.sublist(1);
76+
77+
if (node.children[path] != null) {
78+
return _contains(node.children[path], value);
79+
} else {
80+
return false;
81+
}
82+
}
83+
84+
/// Traverses the path following [value] and marks
85+
/// `node.isValue` to `false` at end. Deletes values eagerly i.e.
86+
/// cleans up any parent nodes that are no longer necessary.
87+
TrieNode _delete(TrieNode node, List<V> value) {
88+
if (value.isEmpty) {
89+
// In case trie is empty and an empty value is passed.
90+
if (node == null) return null;
91+
92+
node.isValue = false;
93+
94+
// Checks all the children. If null, then deletes the node.
95+
var allNull = true;
96+
for (var child in node.children) {
97+
if (child != null) {
98+
allNull = false;
99+
break;
100+
}
101+
}
102+
return allNull ? null : node;
103+
}
104+
105+
// In case trie is empty and some value is passed.
106+
if (node == null) return null;
107+
108+
var path = _indexOf(value.first);
109+
value = value.sublist(1);
110+
111+
// Path to value doesn't exist.
112+
if (node.children[path] == null) {
113+
return node;
114+
}
115+
116+
node.children[path] = _delete(node.children[path], value);
117+
118+
// Delete node if all children are null.
119+
if (node.children[path] == null) {
120+
var allNull = true;
121+
for (var child in node.children) {
122+
if (child != null) {
123+
allNull = false;
124+
break;
125+
}
126+
}
127+
return allNull ? null : node;
128+
}
129+
130+
return node;
131+
}
132+
133+
/// Returns index, which represents the [component] in `node.children`.
134+
int _indexOf(V component) => _components.indexOf(component);
135+
136+
List _split(V value) {
137+
List<V> list = splitter(value);
138+
for (var component in list) {
139+
// TODO: Implement an error class instead.
140+
if (!components.contains(component)) throw ('$component ∉ $components');
141+
}
142+
return list;
143+
}
144+
}
145+
146+
/// Node of the trie, has connection to the set of components as [children].
147+
class TrieNode<V extends Comparable> {
148+
/// If this node represents a value in the trie.
149+
bool isValue = false;
150+
151+
/// Connection to [children].
152+
List<TrieNode> children;
153+
154+
/// Initializes the node to have as many [children]
155+
/// as there are components in the trie.
156+
TrieNode(Set components) {
157+
children = List<TrieNode>(components.length);
158+
}
159+
}

0 commit comments

Comments
 (0)