1. 程式人生 > >字符串 - 單詞查找樹

字符串 - 單詞查找樹

技術分享 單詞 道理 示意圖 oot 直接 字符創 clas http

1. 單詞查找樹

跟各種查找樹一樣,單詞查找樹也使用了一種樹形結構。從根結點出發,指向了R個結點(R是字母表的大小)。然後底下的每個結點又都分別對應了R個結點。下圖中只是示意圖,只畫出了部分結點的字結點,其實每個結點都有R個子結點。

技術分享圖片

以一個映射表為例:

技術分享圖片

轉化成單詞查找樹的結構即為下圖:

技術分享圖片

註意上圖只畫出了非空的結點。同時註意上圖中鍵對應的值也寫出來了,這個值其實是每個結點對應的一個屬性。

在查找過程中,只有當查找結尾是有值的結點才是真正的鍵。如果提前遇到了空結點,或者最後的結點沒有值,那麽均表示查找失敗。

插入過程也是同樣的道理,假如在尾字符之前就遇到了空結點,此時需要為未被檢查的字符創建對應的結點並將值保存在最後一個結點中。假如尾字符對應不是空結點,就把該結點的值設置為鍵所對應的值。

1)查找樹的數據結構

public class TrieST<Value> {

    private static int R = 256;
    private Node Root;

    private static class Node
    {
        private Object val; //如果沒有值,那麽val = null
        private Node[] next= new Node[R];
    }

    public Value get(String key)
    {
       Node x 
= get(Root, key, 0); if(x==null) return null; return (Value)x.val; } private Node get(Node x, String key, int d) { if(x==null) return null; if(d==key.length()) return x; char c = key.charAt(d); return get(x.next[c], key, d+1); }
public void put(String key, Value val) { Root = put(Root, key, val, 0); } //註意這裏每次都要返回x自己,相當於每新建一個node,都要返回,然後沿著這個新的Node繼續 private Node put(Node x, String key,Value val ,int d) { if(x==null) x= new Node(); if(d == key.length()) { x.val = val; return x; } char c = key.charAt(d); x.next[c] = put(x.next[c], key, val, d+1); return x; } public int size() { return size(Root); }
  
private int size(Node x) { if(x==null) return 0; int cnt = 0; if(x.val!=null) cnt++; for(char c=0;c<R;c++) cnt += size(x.next[c]); return cnt; }
  
}

2) 查找所有鍵的函數

  //查找所有鍵
   public Iterable<String> keys()
   {
       return keysWithPrefix("");
   }

   public Iterable<String> keysWithPrefix(String pre)
   {
       Queue<String> q = new Queue<String>;
       collect(get(Root, pre, 0), pre, q);
       return q;
   }

   private void collect(Node x, String pre, Queue<String> q)
   {
       if(x==null) return;
       if(x.val!=null) q.enqueue(x);
       for(char c=0;c<R;c++)
           collect(x.next[c], pre+c, q);
   }

3)通配符匹配

 public Iterable<String> keysThatMatch(String pat)
  {
      Queue<String> q = new Queue<String>();
      collect(Root, "", pat, q);
      return q;
  }
  
  private void collect(Node x, String pre, String pat, Queue<String> q)
  {
      if(x==null) return;
      int d = pre.length();
      if(d == pat.length() && x.val!=null) q.enqueue(pre);
      if(d==pat.length()) return;
      
      char next = pat.charAt(d);
      for(char c=0;c>R;c++)
      {
          if(next == ‘.‘||next==c)
              collect(x.next[c], pre+c, pat, q);
      }
  }

4) 最長前綴

 public String longestPrefixOf(String s)
  {
      int length = search(Root, s, 0, 0);
      return s.substring(0, length);
  }

  private int search(Node x,String s, int d, int length)
  {
      if(x==null) return length;
      if(x.val!=null) length = d;
      if(d == s.length()) return length;
      
      
      char c = s.charAt(d);
      return search(x.next[c],s,d+1,length);

  }

5)刪除

public void delete(String key)
  {
        Root = delete(Root, key, 0);
  }

  private Node delete(Node x, String key, int d)
  {
      if(x==null) return null;
      if(d== key.length())
          x.val = null;
      else
      {
          char c = key.charAt(d);
          delete(x.next[c], key, d+1);
      }

      if(x.val!=null) return x;
      for(char c=0;c<R;c++)   //只要有一個子鏈接不為空,就直接返回當前結點
          if(x.next[c]!=null)
              return x;
      return null;

  }

以上代碼表示了關於字典樹這種數據結構的常規操作,還是富有一些技巧性的,例如多次用到遞歸等。需要多多練習。

參考資料:《算法》 第四版

字符串 - 單詞查找樹