My favorites | Sign in
Project Home Downloads Wiki Issues Source
Checkout   Browse   Changes    
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Text;

namespace GenericTrie
{

/// <summary>
/// A Generic Trie implementation (both key and value are generic). A Wilcard token may be set to select many nodes from the Trie.
/// </summary>
/// <typeparam name="TKey">The type of the key. Eg. "string". The key must implement the IEnumerable(TToken) interface.</typeparam>
/// <typeparam name="TToken">The type of the tokens that make up the key. For a string this is "char", ie a string is made up of characters.
/// must implement IComparable(TToken)</typeparam>
/// <typeparam name="TValue">The type of the values associated with the key.</typeparam>
public class Trie<TKey, TToken, TValue>
where TKey : IEnumerable<TToken>
where TToken : IComparable<TToken>
{
public NodeContainerType ContainerType { get; private set; }
/// <summary>
/// The number of nodes in the trie
/// </summary>
public int TotalNodes { get; private set; }
public bool WildCardIsSet { get; private set; }
private TToken wildcard;
public TToken WildCard
{
get
{
return wildcard;
}
set
{
wildcard = value;
WildCardIsSet = true;
}
}

private TrieNode Root;

public Trie(NodeContainerType ContainerType)
{

Root = new TrieNode(this);// (ContainerType);
this.ContainerType = ContainerType;
}
public Trie()
{

Root = new TrieNode(this);
this.ContainerType = NodeContainerType.SortedDictionary;
}

public void Add(TKey Key, TValue Value)
{

Root.Add(Value, Key.ToArray());
}
public bool ContainsKey(TKey Key)
{
return Root.Contains(Key.ToArray());
}
/// <summary>
/// Return the keys
/// </summary>
/// <param name="Key">The collection of keys</param>
/// <returns></returns>
public List<TToken[]> GetMatchingKeys(TKey Key)
{
TToken[] keyArray = Key.ToArray();

if (Root.Contains(keyArray))
{
return Root.GetMatchingKeys(keyArray, false);
}
else
{
return null;
}
}
public List<TValue> this[TKey Key]
{
get
{
TToken[] keyArray = Key.ToArray();
if (Root.Contains(keyArray))
{
return Root.GetMatchingValues(keyArray, false);
}
else
{
throw new ArgumentException("That key does not exist in the Trie");
}
}
}
public List<TValue> GetMatchingValues(TKey Key)
{
TToken[] keyArray = Key.ToArray();
if (Root.Contains(keyArray))
{
return Root.GetMatchingValues(keyArray, false);
}
return null;
}
public TValue GetValue(TKey key)
{
if (WildCardIsSet)
{
if (key.Contains(WildCard))
{
throw new ArgumentException("Key may not contain wildcard");
}
}

return Root.GetMatchingValues(key.ToArray(), false).First();

}
public List<TToken[]> PrefixKeySearch(TKey Prefix)
{
return Root.GetMatchingKeys(Prefix.ToArray(), true);
}
public List<TValue> PrefixValueSearch(TKey Prefix)
{
return Root.GetMatchingValues(Prefix.ToArray(), true);
}
public List<TToken[]> SuffixSearch()
{
throw new NotImplementedException();
}
private class TrieNode
{
private Trie<TKey, TToken, TValue> container;
private TValue Value;
private TrieNode Parent;
private TToken Key;
public IDictionary<TToken, TrieNode> Children;
public bool Terminal
{
get;
private set;
}
public List<TToken> Prefix
{
get
{
return GetPrefix();
}
}
public List<TToken> Word
{
get
{
List<TToken> word = GetPrefix();
word.Add(this.Key);
return word;
}
}
public TrieNode(Trie<TKey, TToken, TValue> container)
{
this.container = container;


this.container.TotalNodes++;//update the static node count
}
private IDictionary<TToken, TrieNode> CreateChildContainer()
{
IDictionary<TToken, TrieNode> children = null;
switch (container.ContainerType)
{
case NodeContainerType.Dictionary:
children = new Dictionary<TToken, TrieNode>();
break;
case NodeContainerType.LinearAssociativeArray:
children = new LinearAssociativeArray<TToken, TrieNode>();
break;
case NodeContainerType.SortedDictionary:
children = new SortedDictionary<TToken, TrieNode>();
break;
case NodeContainerType.SortedList:
children = new SortedList<TToken, TrieNode>();
break;
}
return children;
}
public override string ToString()
{
return Key.ToString();
}

public List<TToken> GetPrefix()
{

if (Parent != null)
{
Stack<TToken> prefix = new Stack<TToken>();
TrieNode parent = this.Parent;
while (parent.Parent != null)
{
prefix.Push(parent.Key);
parent = parent.Parent;
}
return prefix.ToList();
}
return null;
}
#region Node functions
/// <summary>
/// Test if a node contains a particular child
/// </summary>
/// <param name="Value">The value to test</param>
/// <returns>True if the node contains the chil, otherwise false</returns>
private bool ContainsChild(TToken Value)
{
if (Children == null)
return false;

else if (Children.ContainsKey(Value))
{
return true;
}
return false;
}
/// <summary>
/// Get a child node of this node using its value to find it
/// </summary>
/// <param name="Value">The value of the node to find</param>
/// <returns>The node</returns>
private TrieNode GetChild(TToken Value)
{
if (Children != null && Children.ContainsKey(Value))
{
return Children[Value];
}
return null;
}


#endregion
#region Insertion
public void Add(TValue Value, params TToken[] Key)
{
if (container.WildCardIsSet)
{
foreach (TToken token in Key)
{
if (token.CompareTo(container.WildCard) == 0)
{
throw new ArgumentException("Wildcards may not be inserted to the Trie");
}
}
}
Add(Value, Key, 0);

}
private void Add(TValue Value, TToken[] newKey, int Index)
{

if (this.Children == null || !this.ContainsChild(newKey[Index]))
{
TrieNode newNode = new TrieNode(this.container)//(this.Container)
{
Parent = this,
Key = newKey[Index],
};
if (this.Children == null)
{
this.Children = CreateChildContainer();
}

Children.Add(newNode.Key, newNode);
if (Index < newKey.Length - 1)
{
newNode.Add(Value, newKey, Index + 1);
}
else
{
newNode.Value = Value;
newNode.Terminal = true;
}
}
else
{
if (Index < newKey.Length - 1)
{

TrieNode MatchingChild = GetChild(newKey[Index]);
MatchingChild.Add(Value, newKey, Index + 1);
}
else
{
TrieNode matchingChild = GetChild(newKey[Index]);
matchingChild.Value = Value;
matchingChild.Terminal = true;
}
}
}
#endregion
#region Retrieval
public List<TValue> GetMatchingValues(TToken[] Keys, bool PrefixSearch)
{
List<TValue> Values = new List<TValue>();
return GetMatchingValues(Keys, 0, Values, PrefixSearch);
}
private List<TValue> GetMatchingValues(TToken[] Keys, int Index, List<TValue> Values, bool PrefixSearch)
{
if (Index == Keys.Length)
{
if (this.Terminal)
{
//if (!PrefixSearch)
//{
Values.Add(this.Value);
//}
//else
//{
// Values.Add(this.Value);
// return Values;
//}
}
if (PrefixSearch && Children != null)
{
foreach (KeyValuePair<TToken, TrieNode> value in Children)
{
value.Value.GetMatchingValues(Keys, Index, Values, PrefixSearch);
}
}
return Values;
}
if (Children != null && container.WildCardIsSet && Keys[Index].CompareTo(container.WildCard) == 0)
{
foreach (KeyValuePair<TToken, TrieNode> value in Children)
{
TToken[] TestValues = new TToken[Keys.Length];
Keys.CopyTo(TestValues, 0);
TestValues[Index] = value.Key;
GetMatchingValues(TestValues, Index, Values, PrefixSearch);
}
}
else
{
if (this.ContainsChild(Keys[Index]))
{
return GetChild(Keys[Index]).GetMatchingValues(Keys, Index + 1, Values, PrefixSearch);
}
}

return Values;
}
public List<TToken[]> GetMatchingKeys(TToken[] Keys, bool PrefixSearch)
{
List<TToken[]> matches = new List<TToken[]>();
return GetMatchingKeys(Keys, 0, matches, PrefixSearch);
}
private List<TToken[]> GetMatchingKeys(TToken[] Keys, int Index, List<TToken[]> matches, bool PrefixSearch)
{
if (Index == Keys.Length)
{
if (this.Terminal)
{
matches.Add(this.Word.ToArray());
}
if (PrefixSearch && Children != null)
{
foreach (KeyValuePair<TToken, TrieNode> value in Children)
{
value.Value.GetMatchingKeys(Keys, Index, matches, PrefixSearch);
}
}
return matches;
}
if (Children != null && container.WildCardIsSet && Keys[Index].CompareTo(container.WildCard) == 0)
{
foreach (KeyValuePair<TToken, TrieNode> value in Children)
{
TToken[] TestValues = new TToken[Keys.Length];
Keys.CopyTo(TestValues, 0);
TestValues[Index] = value.Key;
GetMatchingKeys(TestValues, Index, matches, PrefixSearch);
}
}
else
{
if (this.ContainsChild(Keys[Index]))
{
return GetChild(Keys[Index]).GetMatchingKeys(Keys, Index + 1, matches, PrefixSearch);
}
}

return matches;
}

#endregion
#region Content Checking
/// <summary>
/// Check if the trie contains a particular word
/// </summary>
/// <param name="Values">The array of T that compose the word. A Wildcard value is allowed</param>
/// <returns>True if the trie contains the word, otherwise false</returns>
public bool Contains(params TToken[] Values)
{
return Contains(Values, 0);
}
/// <summary>
/// Recursive sub function for the Contains method group
/// </summary>
/// <param name="Values">The value composing the word to check</param>
/// <param name="Index">The index into the word being evaluated</param>
/// <returns>True if the word is found, otherwise false</returns>
private bool Contains(TToken[] Values, int Index)
{
if (Index == Values.Length)
{
if (Terminal)
{
return true;
}
else
{
return false;
}
}

if (Children != null && container.WildCardIsSet && Values[Index].CompareTo(container.WildCard) == 0)
{

foreach (KeyValuePair<TToken, TrieNode> value in Children)
{
TToken[] TestValues = new TToken[Values.Length];
Values.CopyTo(TestValues, 0);
TestValues[Index] = value.Key;
bool Contained = Contains(TestValues, Index);
if (Contained == true)
{
return true;
}
}
}
else
{
if (this.ContainsChild(Values[Index]))
{
return GetChild(Values[Index]).Contains(Values, Index + 1);
}
}

return false;
}
#endregion
}
}
}

Change log

r13 by scottm361 on Feb 5, 2011   Diff
fixed bug in Contains() where true was
returned even if the last found node wasnt
terminal.

fixed bug in PrefixKeySearch where
substrings would be the only result
returned
Go to: 
Project members, sign in to write a code review

Older revisions

r12 by scottm361 on Feb 3, 2011   Diff
-upgraded to VS2010
-fixed bug where a partial key would
return true for Contains()
r11 by scottm361 on Aug 20, 2010   Diff
implemented prefix search
r9 by scottm361 on Aug 18, 2010   Diff
removed static members from
Trie<K,T,V> class
removed argument exception for
GetMatchingValues, not returns null if
no values are found.
...
All revisions of this file

File info

Size: 16487 bytes, 453 lines
Powered by Google Project Hosting