Skip to content

Commit 1920ad5

Browse files
authored
Merge pull request #27 from happy-san/trie
Trie
2 parents f0ee15a + 7218bdc commit 1920ad5

File tree

4 files changed

+225
-12
lines changed

4 files changed

+225
-12
lines changed

lib/trees/binary_tree_adt.dart

+3-3
Original file line numberDiff line numberDiff line change
@@ -38,21 +38,21 @@ abstract class BinaryTreeADT<N extends BinaryNodeADT, V extends Comparable>
3838
@override
3939
void nullify() => root = null;
4040

41-
@override
41+
/// In Order Traversal.
4242
List<V> inOrder() {
4343
var result = <V>[];
4444
_inOrder(root, result);
4545
return result;
4646
}
4747

48-
@override
48+
/// PostOrder Traversal.
4949
List<V> postOrder() {
5050
var result = <V>[];
5151
_postOrder(root, result);
5252
return result;
5353
}
5454

55-
@override
55+
/// PreOrder Traversal.
5656
List<V> preOrder() {
5757
var result = <V>[];
5858
_preOrder(root, result);

lib/trees/tree_adt.dart

-9
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,6 @@ abstract class TreeADT<N extends NodeADT, V extends Comparable> {
2828
/// Deletes [value] from the tree and updates it's [root].
2929
void delete(V value);
3030

31-
/// In Order Traversal.
32-
List<V> inOrder();
33-
3431
/// Empty the tree.
3532
void nullify();
36-
37-
/// PostOrder Traversal.
38-
List<V> postOrder();
39-
40-
/// PreOrder Traversal.
41-
List<V> preOrder();
4233
}

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+
}

test/trie/trie_test.dart

+63
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import 'package:test/test.dart';
2+
import 'package:algorithms/trie/trie.dart';
3+
4+
void main() {
5+
Trie emptyTrie, trie, customTrie;
6+
7+
setUp(() {
8+
emptyTrie = Trie.ofAlphabets();
9+
10+
trie = Trie.ofAlphabets();
11+
trie.add('algorithms');
12+
13+
customTrie = Trie(
14+
{'a', 'b', 'f', 'o', 'r', 'z'}, (value) => value.toString().split(''));
15+
customTrie.add('foo');
16+
});
17+
18+
test('Components', () {
19+
expect(trie.components,
20+
equals({...List.generate(26, (i) => String.fromCharCode(122 - i))}));
21+
22+
expect(customTrie.components, equals({'f', 'o', 'b', 'a', 'r', 'z'}));
23+
});
24+
25+
test('Empty trie', () {
26+
expect(emptyTrie.isEmpty, isTrue);
27+
expect(trie.isEmpty, isFalse);
28+
});
29+
30+
test('Add value', () {
31+
trie.add('hi');
32+
expect(trie.root.children[7].children[8].isValue, isTrue);
33+
34+
customTrie.add('bar');
35+
expect(customTrie.root.children[1].children[0].children[4].isValue, isTrue);
36+
37+
customTrie.add('baz');
38+
expect(customTrie.root.children[1].children[0].children[5].isValue, isTrue);
39+
});
40+
41+
test('Contains value', () {
42+
expect(emptyTrie.contains('test'), isFalse);
43+
44+
expect(trie.contains('algorithm'), isFalse);
45+
expect(trie.contains('algorithms'), isTrue);
46+
47+
expect(customTrie.contains('boar'), isFalse);
48+
expect(customTrie.contains('foo'), isTrue);
49+
});
50+
51+
test('Delete value', () {
52+
emptyTrie.delete('');
53+
emptyTrie.delete('test');
54+
55+
expect(trie.contains('algorithms'), isTrue);
56+
trie.delete('algorithms');
57+
expect(trie.contains('algorithms'), isFalse);
58+
59+
expect(customTrie.contains('foo'), isTrue);
60+
customTrie.delete('foo');
61+
expect(customTrie.contains('foo'), isFalse);
62+
});
63+
}

0 commit comments

Comments
 (0)