日本好好热aⅴ|国产99视频精品免费观看|日本成人aV在线|久热香蕉国产在线

  • <cite id="ikgdy"><table id="ikgdy"></table></cite>
    1. 西西軟件下載最安全的下載網(wǎng)站、值得信賴的軟件下載站!

      首頁編程開發(fā)其它知識 → 字典樹Trie和三叉搜索樹Ternary Tree的學(xué)習(xí)總結(jié)

      字典樹Trie和三叉搜索樹Ternary Tree的學(xué)習(xí)總結(jié)

      相關(guān)軟件相關(guān)文章發(fā)表評論 來源:西西整理時間:2012/12/31 2:39:04字體大小:A-A+

      作者:西西點擊:0次評論:0次標簽:

      • 類型:源碼相關(guān)大。510KB語言:中文 評分:6.0
      • 標簽:
      立即下載
      3 頁 Ternary Tree的定義和實現(xiàn)

      前面,我們介紹了Trie樹結(jié)構(gòu),它的實現(xiàn)簡單但空間效率低。如果要支持26個英文字母,每個節(jié)點就要保存26個指針,假若我們還要支持國際字符、標點符號、區(qū)分大小寫,內(nèi)存用量就會急劇上升,以至于不可行。

      由于節(jié)點數(shù)組中保存的空指針占用了太多內(nèi)存,我們遇到的困難與此有關(guān),因此可以考慮改用其他數(shù)據(jù)結(jié)構(gòu)去代替,比如用hash map。然而,管理成千上萬個hash map肯定也不是什么好主意,而且它使數(shù)據(jù)的相對順序信息丟失,所以我們還是去看看另一種更好解法吧——Ternary Tree。

      接下來,我們將介紹三叉搜索樹,它結(jié)合字典樹的時間效率和二叉搜索樹的空間效率優(yōu)點。

      Ternary Tree的實現(xiàn)

      三叉搜索樹使用了一種聰明的手段去解決Trie的內(nèi)存問題(空的指針數(shù)組)。為了避免多余的指針占用內(nèi)存,每個Trie節(jié)點不再用數(shù)組來表示,而是表示成“樹中有樹”。Trie節(jié)點里每個非空指針都會在三叉搜索樹里得到屬于它自己的節(jié)點。

      接下來,我們將實現(xiàn)三叉搜索樹的節(jié)點類,具體實現(xiàn)如下:

      /// <summary>
      /// The node type.
      /// Indicates the word completed or not.
      /// </summary>
      public enum NodeType
      {
          COMPLETED,
          UNCOMPLETED
      };
      
      
      /// <summary>
      /// The tree node.
      /// </summary>
      public class Node
      {
          internal char Word { get; set; }
      
          internal Node LeftChild, CenterChild, RightChild;
      
          internal NodeType Type { get; set; }
      
          public Node(char ch, NodeType type)
          {
              Word = ch;
              Type = type;
          }
      }

      由于三叉搜索樹包含三種類型的箭頭。第一種箭頭和Trie里的箭頭是一樣的,也就是圖2里畫成虛線的向下的箭頭。沿著向下箭頭行進,就意味著“匹配上”了箭頭起始端的字符。如果當(dāng)前字符少于節(jié)點中的字符,會沿著節(jié)點向左查找,反之向右查找。

      接下來,我們將定義Ternary Tree類型,并且添加Insert(),F(xiàn)ind()和FindSimilar()方法。

      /// <summary>
      /// The ternary tree.
      /// </summary>
      public class TernaryTree
      {
          private Node _root;
      
          ////private string _prefix;
      
          private HashSet<string> _hashSet;
      
          /// <summary>
          /// Inserts the word into the tree.
          /// </summary>
          /// <param name="s">The word need to insert.</param>
          /// <param name="index">The index of the word.</param>
          /// <param name="node">The tree node.</param>
          private void Insert(string s, int index, ref Node node)
          {
              if (null == node)
              {
                  node = new Node(s[index], NodeType.UNCOMPLETED);
              }
      
              if (s[index] < node.Word)
              {
                  Node leftChild = node.LeftChild;
                  this.Insert(s, index, ref node.LeftChild);
              }
              else if (s[index] > node.Word)
              {
                  Node rightChild = node.RightChild;
                  this.Insert(s, index, ref node.RightChild);
              }
              else
              {
                  if (index + 1 == s.Length)
                  {
                      node.Type = NodeType.COMPLETED;
                  }
                  else
                  {
                      Node centerChild = node.CenterChild;
                      this.Insert(s, index + 1, ref node.CenterChild);
                  }
              }
          }
      
          /// <summary>
          /// Inserts the word into the tree.
          /// </summary>
          /// <param name="s">The word need to insert.</param>
          public void Insert(string s)
          {
              if (string.IsNullOrEmpty(s))
              {
                  throw new ArgumentException("s");
              }
      
              Insert(s, 0, ref _root);
          }
      
          /// <summary>
          /// Finds the specified world.
          /// </summary>
          /// <param name="s">The specified world</param>
          /// <returns>The corresponding tree node.</returns>
          public Node Find(string s)
          {
              if (string.IsNullOrEmpty(s))
              {
                  throw new ArgumentException("s");
              }
      
              int pos = 0;
              Node node = _root;
              _hashSet = new HashSet<string>();
              while (node != null)
              {
                  if (s[pos] < node.Word)
                  {
                      node = node.LeftChild;
                  }
                  else if (s[pos] > node.Word)
                  {
                      node = node.RightChild;
                  }
                  else
                  {
                      if (++pos == s.Length)
                      {
                          _hashSet.Add(s);
                          return node.CenterChild;
                      }
      
                      node = node.CenterChild;
                  }
              }
      
              return null;
          }
      
          /// <summary>
          /// Get the world by dfs.
          /// </summary>
          /// <param name="prefix">The prefix of world.</param>
          /// <param name="node">The tree node.</param>
          private void DFS(string prefix, Node node)
          {
              if (node != null)
              {
                  if (NodeType.COMPLETED == node.Type)
                  {
                      _hashSet.Add(prefix + node.Word);
                  }
      
                  DFS(prefix, node.LeftChild);
                  DFS(prefix + node.Word, node.CenterChild);
                  DFS(prefix, node.RightChild);
              }
          }
      
          /// <summary>
          /// Finds the similar world.
          /// </summary>
          /// <param name="s">The prefix of the world.</param>
          /// <returns>The world has the same prefix.</returns>
          public HashSet<string> FindSimilar(string s)
          {
              Node node = this.Find(s);
              this.DFS(s, node);
              return _hashSet;
          }
      }

      和Trie類似,我們在TernaryTree 類中,定義了Insert(),F(xiàn)ind()和FindSimilar()方法,它包含兩個字段分別是:_root和_hashSet,_root用來保存Trie樹的根節(jié)點,我們使用_hashSet保存前綴匹配的所有單詞。

      由于三叉搜索樹每個節(jié)點只有三個叉,所以我們在進行節(jié)點插入操作時,只需判斷插入的字符與當(dāng)前節(jié)點的關(guān)系(少于,等于或大于)插入到相應(yīng)的節(jié)點就OK了。

      我們使用之前的例子,把字符串AB,ABBA,ABCD和BCD插入到三叉搜索樹中,首先往樹中插入了字符串AB,接著我們插入字符串ABCD,由于ABCD與AB有相同的前綴AB,所以C節(jié)點都是存儲到B的CenterChild中,D存儲到C的CenterChild中;當(dāng)插入ABBA時,由于ABBA與AB有相同的前綴AB,而B字符少于字符C,所以B存儲到C的LeftChild中;當(dāng)插入BCD時,由于字符B大于字符A,所以B存儲到C的RightChild中。

      圖4三叉搜索樹

      我們注意到插入字符串的順序會影響三叉搜索樹的結(jié)構(gòu),為了取得最佳性能,字符串應(yīng)該以隨機的順序插入到三叉樹搜索樹中,尤其不應(yīng)該按字母順序插入,否則對應(yīng)于單個Trie

      節(jié)點的子樹會退化成鏈表,極大地增加查找成本。當(dāng)然我們還可以采用一些方法來實現(xiàn)自平衡的三叉樹。

      由于樹是否平衡取決于單詞的讀入順序,如果按排序后的順序插入,則該方式生成的樹是最不平衡的。單詞的讀入順序?qū)τ趧?chuàng)建平衡的三叉搜索樹很重要,所以我們通過選擇一個排序后數(shù)據(jù)集合的中間值,并把它作為開始節(jié)點,通過不斷折半插入中間值,我們就可以創(chuàng)建一棵平衡的三叉樹。我們將通過方法BalancedData()實現(xiàn)數(shù)據(jù)折半插入,具體實現(xiàn)如下:

      /// <summary>
      /// Balances the ternary tree input data.
      /// </summary>
      /// <param name="file">The file saves balanced data.</param>
      /// <param name="orderList">The order data list.</param>
      /// <param name="offSet">The offset.</param>
      /// <param name="len">The length of data list.</param>
      public void BalancedData(StreamWriter file, IList<KeyValuePair<int, string>> orderList, int offSet, int len)
      {
          if (len < 1)
          {
              return;
          }
      
          int midLen = len >> 1;
      
          // Write balanced data into file.
          file.WriteLine(orderList[midLen + offSet].Key + " " + orderList[midLen + offSet].Value);
      
          BalancedData(file, orderList, offSet, midLen);
          BalancedData(file, orderList, offSet + midLen + 1, len - midLen - 1);
      }

      上面,我們定義了方法BalancedData(),它包含四個參數(shù)分別是:file,orderList,offSet和len。File寫入平衡排序后的數(shù)據(jù)到文本文件。orderList按順序排序后的數(shù)據(jù)。offSet偏移量。Len插入的數(shù)據(jù)量。

      同樣我們創(chuàng)建一棵三叉搜索樹,然后把兩千個英語單詞插入到三叉搜索樹中,最后我們查找前綴為“ab”的所有單詞包括前綴本身。

      public class Program
      {
          public static void Main()
          {
              // Creates a file object.
              var file = File.ReadAllLines(Environment.CurrentDirectory + "//1.txt");
      
              // Creates a trie tree object.
              var ternaryTree = new TernaryTree();
      
              var dictionary = new Dictionary<int, string>();
              foreach (var item in file)
              {
                  var sp = item.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
                  ternaryTree.Insert(sp.LastOrDefault().ToLower());
              }
      
              Stopwatch watch = Stopwatch.StartNew();
      
              // Gets words have the same prefix.
              var similarWords = ternaryTree.FindSimilar("ab");
              foreach (var similarWord in similarWords)
              {
                  Console.WriteLine("Similar word: {0}", similarWord);
              }
      
              watch.Stop();
              Console.WriteLine("Time consumes: {0} ms", watch.ElapsedMilliseconds);
              Console.WriteLine("Similar word: {0}", similarWords.Count);
              Console.Read();
          }
      }

      圖5匹配結(jié)果

      我們在1.txt文本文件中通過正則表達式(^:z ab+)查找前綴為ab的所有單詞,剛好就是上面9個單詞。

        相關(guān)評論

        閱讀本文后您有什么感想? 已有人給出評價!

        • 8 喜歡喜歡
        • 3 頂
        • 1 難過難過
        • 5 囧
        • 3 圍觀圍觀
        • 2 無聊無聊

        熱門評論

        最新評論

        發(fā)表評論 查看所有評論(0)

        昵稱:
        表情: 高興 可 汗 我不要 害羞 好 下下下 送花 屎 親親
        字數(shù): 0/500 (您的評論需要經(jīng)過審核才能顯示)
        推薦文章

        沒有數(shù)據(jù)