Trie implementation in AS3

In this post, we will implement the Trie data structure in ActionScript 3. Then we will draw it to get a nice picture of this data structure.

What Trie is

Let’s say, our program gets a thousand words, which we have to store into some data structure. After, we will recieve requests, whether some word is contained in this set. If we store these words in linear array, it will be very unefficient (large memory and long running time when searching for a word). The model of Trie is more efficient. Read more on Wikipedia page.

Class TNode

Whad do I want

I want an ability to easily create a Trie (var trie = new …), easily add words (trie.AddWord(“ahoj”);). Easily detect, whether my trie contains some word (if(trie.Contains(“hello”)) …).

How to store children in trie node

Each node can have multiple children, their count is from 0 up to length of an alphabet. Alphabets are often very long (26 – small letters, 256 – ASCII characters, $2^{16}, 2^{24}, 2^{32}$ – Unicode characters). But nodes mostly don’t have all the children from an alphabet. I decided to have a fixed-length array and to hash pointers into it. In the example below, I have an array of 4 pointers, I store the pointer on [char code] mod 4. When collision happens, I put pointers into a linked list.

Basic class

package
{
   public class TNode
   {
      // the array of 4 children of this node
      public var cn:Vector.<TNode> = new Vector.<TNode>(4, true);
      public var c:String;   // character stored in the node
      public var next:TNode; // pointer to the next node (in linked list)
      public var isEnd:Boolean = false; // whether there is the end of word
  
      public function TNode(c:String):void
      {
         this.c = c; // assign the parameter to "c"
      }
   }
}

Searching a child for a character

Since children are in fixed-length array + in linked lists, it is not so easy to get them, so we write this method:
public function GetChild(s:String):TNode
{
   // at first, take a look at the array "cn"
   var n:TNode = cn[ s.charCodeAt(0)% 4 ];
   if ( n == null) return null; // there is no child for "s"
   else    // there is some child, we loop through the linked list
      while (n.next != null)  
      {
         if (n.c == s) return n;
         n = n.next;
      }
   if (n.c == s) return n; // it was at the end of LL
   return null; // it wasn't anywhere
}

Adding a word into a Trie

At first, we read the first character of the word. If there already is a child for this charater, we add the rest of the word to this child. If not, we create a new node and add the rest of the word into it.
public function AddWord(s:String):void
{
   // look for child for this char
   var n:TNode = GetChild(s.charAt(0));
   if(n==null) // there is no child
   {
      n = new TNode(s.charAt(0)); // we chreate one
      var pos:int = s.charCodeAt(0)% 4; // where should it be
      if(cn[pos] == null) cn[pos] = n;  // free position
      else // not free - add it to LL
      {
         var no:TNode = cn[ pos ];
         while(no.next != null) no = no.next;
         no.next = n;
      }
   }
   // add the rest of the word to it or set the flag
   if(s.length>1) n.AddWord(s.substring(1,s.length));
   else n.isEnd = true; // end of the word
}

Does Trie contain the word

public function Contains(s:String):Boolean
{
   // at the end of recursion we return isEnd
   if(s.length == 0) return isEnd;
   // we look at the proper child
   var n:TNode = GetChild(s.charAt(0));
   if(n==null) return false; // no such child
   else return n.Contains(s.substring(1,s.length)); // ask the child
}

Drawing a trie

We draw a Trie using very common code as when I was drawing Binary search tree.
function drawTrie(n:TNode, x:int, y:int, w:int, place:Sprite)
{
   var i:int;
   // at first, we add chilren to array
   var nodes:Array = new Array();
   for(i = 0; i<4; i++)
   {
      var no:TNode = n.cn[i]; if(no == null) continue;
      while(no != null){ nodes.push(no); no = no.next;}
   }
   var sW:int = w/nodes.length; // width for each node
   for (i = 0; i < nodes.length; i++)
   {
      with(place.graphics)  // line to child
      {
         lineStyle(3,0x00E1FF);
         moveTo(x, y);
         lineTo(x-w/2 + i*sW + sW/2, y+60);
      }
      // drawing a child
      drawTrie(nodes[i], x-w/2 + i*sW + sW/2, y+60, sW, place); 
   }

   // draw a round for this node
   with(place.graphics)
   {
      if(n.isEnd) lineStyle(4, 0x000000); // když je koncový
      else lineStyle(2, 0x333333);  // když není - tenčí okraj
      beginFill(0x6D00D9);
      drawEllipse(x-14, y-14, 28, 28);
      endFill();
   }
 
   // add text field with character
   var tf:TextField = new TextField();
   tf.text = n.c; tf.x = x-tf.textWidth; tf.y = y-14;
   place.addChild(tf);
}

Old comments (closed because of spam)

8 Comments

  1. LB said:

    Thanks for such nice visual presentation. It helps me a lot to understand, and implement a trie.

    July 4th, 2011
  2. Juan dela Cruz said:

    ..wow

    May 7th, 2012
  3. Juan dela Cruz said:

    where’s the download link?

    May 7th, 2012
  4. Juan dela Cruz said:

    kelangan ko nyan

    May 7th, 2012
  5. Ivan Kuckir said:

    Juan: How can I help you? You can download SWF and use it locally. You can copy the code and use it. If you made a donation, I can give you other things, what exactly do you need?

    May 7th, 2012
  6. Girish S said:

    Hi this is too good….
    Could u pls give me the confirmation that will i b able to implement the same AC algorithm in verilog code……… pls do reply m waiting..

    August 22nd, 2012
  7. Luis said:

    why does the constructor requires a string value ?

    May 3rd, 2013
  8. Ivan Kuckir said:

    Luis: It is because ActionScript does not have a character type, so I decided to work with one letter strings.

    May 3rd, 2013