11package dataStructures .trie ;
22
3+ import java .util .ArrayList ;
4+ import java .util .List ;
5+
36/**
4- * Implementation of Trie structure.
5- * Supports the follwing common operations (see below for doc):
6- * insert(String word)
7- * search(String word)
8- * startsWith(String prefix)
9- * prune(String word)
7+ * Implementation of a Trie; Here we consider strings (not case-sensitive)
108 */
119public class Trie {
1210 private final TrieNode root ;
@@ -15,99 +13,114 @@ public Trie() {
1513 root = new TrieNode ();
1614 }
1715
18- /**
19- * Insert a word into the trie; converts word to
20- * to lower-case characters before insertion.
21- *
22- * @param word the string to be inserted
23- */
2416 public void insert (String word ) {
25- word = word .toLowerCase ();
26- System .out .printf ("~~~~~~~Inserting '%s'~~~~~~~%n" , word );
27- TrieNode node = root ;
17+ word = word .toLowerCase (); // ignore case-sensitivity
18+ TrieNode trav = root ;
2819 for (int i = 0 ; i < word .length (); i ++) {
2920 char curr = word .charAt (i );
30- if (!node .containsKey (curr )) {
31- node . insertKey (curr );
21+ if (!trav . children .containsKey (curr )) {
22+ trav . children . put (curr , new TrieNode ()); // recall, the edges represent the characters
3223 }
33- node = node . getNext (curr ); // go to the subsequent node!
24+ trav = trav . children . get (curr );
3425 }
35- node . makeEnd ();
26+ trav . isEnd = true ; // set word
3627 }
3728
38- /**
39- * Search for a word (converted to lower-case) in the trie.
40- *
41- * @param word the string to look for
42- * @return boolean representing whether the word was found
43- */
4429 public boolean search (String word ) {
45- word .toLowerCase ();
46- System .out .printf ("~~~~~~~Searching '%s'~~~~~~~%n" , word );
47- TrieNode node = root ;
30+ word = word .toLowerCase ();
31+ TrieNode trav = root ;
4832 for (int i = 0 ; i < word .length (); i ++) {
4933 char curr = word .charAt (i );
50- if (node .containsKey (curr )) {
51- node = node .getNext (curr );
52- } else {
34+ if (!trav .children .containsKey (curr )) {
5335 return false ;
5436 }
37+ trav = trav .children .get (curr );
5538 }
56- return node .isEnd () ;
39+ return trav .isEnd ;
5740 }
5841
59- /**
60- * Search for a prefix (converted to lower-case) in the trie.
61- * Note: very similar in implementation to search method
62- * except the search here does not need to look for end flag
63- *
64- * @param prefix the string to look for
65- * @return boolean representing whether the prefix exists
66- */
67- public boolean startsWith (String prefix ) {
68- prefix = prefix .toLowerCase ();
69- System .out .printf ("~~~~~~~Looking for prefix '%s'~~~~~~~%n" , prefix );
70- TrieNode node = root ;
71- for (int i = 0 ; i < prefix .length (); i ++) {
72- char curr = prefix .charAt (i );
73- if (node .containsKey (curr )) {
74- node = node .getNext (curr );
75- } else {
76- return false ;
42+ public void delete (String word ) {
43+ word = word .toLowerCase ();
44+ TrieNode trav = root ;
45+ for (int i = 0 ; i < word .length (); i ++) {
46+ char curr = word .charAt (i );
47+ if (!trav .children .containsKey (curr )) {
48+ return ; // word does not exist in trie, so just return
7749 }
50+ trav = trav .children .get (curr );
7851 }
79- return true ;
52+ trav . isEnd = false ; // remove word from being tracked
8053 }
8154
82- /**
83- * Removes a word from the trie by toggling the end flag;
84- * if any of the end nodes (next nodes relative to current)
85- * do not hold further characters, repetitively prune the trie
86- * by removing these nodes from the hashmap of the current node.
87- * Note: This method is useful in optimizing searching for a set of known words
88- * especially when the data to be traversed has words that are similar in spelling/
89- * repeated words which might have been previously found.
90- *
91- * @param word the word to be removed
92- */
93- public void prune (String word ) {
94- word = word .toLowerCase ();
95- System .out .printf ("~~~~~~~Removing '%s'~~~~~~~%n" , word );
96- TrieNode node = root ;
97- TrieNode [] track = new TrieNode [word .length ()];
55+ // ABOVE ARE STANDARD METHODS OF A TYPICAL TRIE IMPLEMENTATION
56+ // BELOW IMPLEMENTS TWO MORE COMMON / USEFUL METHODS FOR TRIE; IN PARTICULAR, NOTE THE PRUNING METHOD
57+
58+ public void deleteAndPrune (String word ) {
59+ List <TrieNode > trackNodes = new ArrayList <>();
60+ TrieNode trav = root ;
9861 for (int i = 0 ; i < word .length (); i ++) {
9962 char curr = word .charAt (i );
100- track [i ] = node ;
101- node = node .getNext (curr );
63+ if (!trav .children .containsKey (curr )) {
64+ return ; // word does not exist in trie
65+ }
66+ trackNodes .add (trav );
67+ trav = trav .children .get (curr );
10268 }
103- node .removeEnd ();
69+ trav .isEnd = false ;
70+
71+ // now we start pruning
10472 for (int i = word .length () - 1 ; i >= 0 ; i --) {
10573 char curr = word .charAt (i );
106- if (track [i ].getNext (curr ).getCharacters ().size () > 0 ) {
107- break ; // done further nodes are required
108- } else {
109- track [i ].getCharacters ().remove (curr );
74+ TrieNode nodeBeforeCurr = trackNodes .get (i );
75+ TrieNode nextNode = nodeBeforeCurr .children .get (curr );
76+ if (nextNode .children .size () == 0 ) { // this node essentially doesn't track anything, remove
77+ nodeBeforeCurr .children .remove (curr );
78+ } else { // children.size() > 0; i.e. this node is still useful; no need to further prune upwards
79+ break ;
80+ }
81+ }
82+ return ;
83+ }
84+
85+ public List <String > wordsWithPrefix (String prefix ) {
86+ List <String > ret = new ArrayList <>();
87+ TrieNode trav = root ;
88+ for (int i = 0 ; i < prefix .length (); i ++) {
89+ char curr = prefix .charAt (i );
90+ if (!trav .children .containsKey (curr )) {
91+ return ret ; // no words with this prefix
92+ }
93+ trav = trav .children .get (curr );
94+ }
95+ List <StringBuilder > allSuffix = getAllSuffixFromNode (trav );
96+ for (StringBuilder sb : allSuffix ) {
97+ ret .add (prefix + sb .toString ());
98+ }
99+ return ret ;
100+ }
101+
102+ public List <String > getAllWords () {
103+ List <StringBuilder > allWords = getAllSuffixFromNode (root );
104+ List <String > ret = new ArrayList <>();
105+ for (StringBuilder sb : allWords ) {
106+ ret .add (sb .toString ());
107+ }
108+ return ret ;
109+ }
110+
111+ private List <StringBuilder > getAllSuffixFromNode (TrieNode node ) {
112+ List <StringBuilder > ret = new ArrayList <>();
113+ if (node .isEnd ) {
114+ ret .add (new StringBuilder ("" ));
115+ }
116+ for (char c : node .children .keySet ()) {
117+ TrieNode nextNode = node .children .get (c );
118+ List <StringBuilder > allSuffix = getAllSuffixFromNode (nextNode );
119+ for (StringBuilder sb : allSuffix ) {
120+ sb .insert (0 , c ); // insert c at the front
121+ ret .add (sb );
110122 }
111123 }
124+ return ret ;
112125 }
113126}
0 commit comments