Trie v AS3
Dnes se ve flashi pokusíme implementovat datovou strukturu Trie. Kromě samotné trie ji navíc nějak hezky vykreslíme, abychom dostali přehledné znázornění této struktury.
Něco o Trii
Řekněme, že náš program dostane tisíc slov, která musíme uložit do nějaké struktury. Poté nám budou chodit dotazy, zda naše struktura obsahuje konkrétní slovo. Uložení prvku do lineárního pole by bylo paměťově náročné, zároveň při hledání slova bychom museli projít všechny prvky. Trie je na tom o mnoho lépe. Sice přidání slova není tak rychlé jako u pole, ale hledání je výrazně rychlejší. Pokud v každém uzlu uchováváme pole o velikosti celé abecedy, jdeme od kořene přímo k listu, složitost je rovná délce slova. Více ve článku na Wikipedii.Třída TNode
Co od trie chci
Chci si trii snadno vytvářet (var trie = new ...), snadno přidávat slova (trie.AddWord("ahoj");). Chci snadno zjišťovat, zda obsahuje nějaké slovo (if(trie.Contains("ahoj")) ...).
Jak ukládat potomky uzlu
Každý uzel ve trii může mít potomků od 0 až do velikosti abecedy. Abecedy jsou často velké (26 - písmena ang. abecedy, 256 - ASCII znaky, $2^{16}, 2^{24}, 2^{32}$ - znaky Unicode atd.). Avšak uzel většinou neobsahuje všechny tyto potomky. Paměťově efektivnější použít nějaké menší pole pevné velikosti, do kterého budeme prvky hešovat. Níže ukládám prvek na pozici (kód znaku) modulo 4. Kolize řeším vytvářením spojového seznamu.Základní třída
package { public class TNode { // pole 4 potomků tohoto uzlu public var cn:Vector.= new Vector. (4, true); public var c:String; // znak uložený v uzlu public var next:TNode; // ukazatel na další prvek na stejné úrovni public var isEnd:Boolean = false; // zda je uzel koncem slova public function TNode(c:String):void { this.c = c; // přiřadím znak do "c" } } }
Vyhledání potomka pro daný znak
Jelikož potomky máme v poli pevné velikosti a navíc ve spojových seznamech, není jejich vyhledání úplně triviální, proto si raději napíšeme metodu.
public function GetChild(s:String):TNode { // nejdříve se podíváme do pole var n:TNode = cn[ s.charCodeAt(0)% 4 ]; if ( n == null) return null; // nebyl v poli else // byl v poli, hledáme ve spojáku while (n.next != null) { if (n.c == s) return n; n = n.next; } if (n.c == s) return n; // byl na konci spojáku return null; // nebyl nikde }
Přidání slova do trie
Nejdříve si přečteme první znak slova. Buď pro tento znak už máme potomka, nebo ne (potom ho vytvoříme). Tomuto potomku sdělíme mu, aby si přidal zbytek slova.
public function AddWord(s:String):void { // podíváme se, zda nemáme potřebného potomka var n:TNode = GetChild(s.charAt(0)); if(n==null) // nemáme { n = new TNode(s.charAt(0)); // vytvoříme ho var pos:int = s.charCodeAt(0)% 4; // kam ho dáme if(cn[pos] == null) cn[pos] = n; // místo je volné else // není volné - dáme do spojáku { var no:TNode = cn[ pos ]; while(no.next != null) no = no.next; no.next = n; } } // když není konec slova, přidáme zbytek do "n" if(s.length>1) n.AddWord(s.substring(1,s.length)); else n.isEnd = true; // konec slova }
Zda trie obsahuje slovo
public function Contains(s:String):Boolean { // když jsme na dně rekurze, vrátíme "isEnd" if(s.length == 0) return isEnd; // podíváme se na potomka s daným znakem var n:TNode = GetChild(s.charAt(0)); if(n==null) return false; // potomek není else return n.Contains(s.substring(1,s.length)); // potomek je }
Vykreslení trie
Trii vykreslujeme podobným způsobem, jako Binární vyhledávací strom.
function vykresli(n:TNode, x:int, y:int, w:int, place:Sprite) { var i:int; // potomky uzlu si dám do nového pole 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; // místo pro každý uzel for (i = 0; i < nodes.length; i++) { with(place.graphics) // čára k potomkovi { lineStyle(3,0x00E1FF); moveTo(x, y); lineTo(x-w/2 + i*sW + sW/2, y+60); } // a vykreslení potomka vykresli(nodes[i], x-w/2 + i*sW + sW/2, y+60, sW, place); } // nakreslíme kroužek pro daný uzel 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(); } // přidáme textové pole s písmenkem var tf:TextField = new TextField(); tf.text = n.c; tf.x = x-tf.textWidth; tf.y = y-14; place.addChild(tf); }
Okomentovat