字典樹學習總結

1、字典樹

字典樹別名有Tire樹、前綴樹(prefix tree)、鍵樹,是一種多叉樹結構:
java

上圖演示的是a、to、hero三個單詞的字典樹結構,從上圖咱們能夠概括出Trie樹的基本性質node

  • 根節點不包含字符,出根節點之外的每個子節點都包含一個字符。
  • 從根節點到某一個節點,路徑上通過的字符連起來,爲該節點所對應的字符串。
  • 每一個節點的全部子節點包含的字符互不相同。

由此咱們得知一個字典樹的節點應該包含字符所鏈接子節點的指針,不過通常還要設置一個標誌,用來標誌該節點處是否構成一個單詞數組

struct trie_node{
     int count;//0:不構成一個單詞,>0:構成一個單詞
     struct trie_node *children[26]; //如上圖,'to'中o對應節點的children數組值全爲NULL
}

能夠看出,Trie樹的關鍵字通常都是字符串,並且Trie樹把每一個關鍵字都保存在一個路徑上,而不是一個節點中。另外,有兩個公共前綴的關鍵字,在Trie樹中前綴部分路徑相同。因此Trie又稱爲前綴樹函數

字典樹的應用:

  • 字符串檢索
    檢索、查詢功能是字典樹最原始的功能之一,原理就是從根節點開始一個個字符比較
    一、比較過程當中字典樹顯示該節點爲NULL,則說明該字符串在字典樹中不存在。
    二、比較到字符串最後一個節點時,檢查當前字典樹節點標識位是否標識爲一個關鍵字。ui

  • 詞頻統計
    這裏咱們只需將count修改爲一個計數器,對每個關鍵字進行插入操做時,count++;搜索引擎

  • 字符串排序
    咱們從字典樹樹結構能夠看出,它每個節點數組長度都是26,當字符存在時,數組相應位置不爲NULL,因此咱們只需先序遍歷輸出Trie樹中插入的全部關鍵字便可。編碼

  • 前綴匹配
    例如:找出一個字符串集合中全部以ab開頭的字符串。咱們只需用全部字符串構造一個Trie樹,而後輸出以a->b開頭的路徑上的關鍵字便可。通常咱們用的搜索引擎也體現了這種思想:指針

字典樹例題:

單詞的壓縮編碼code

1、題目:

給定一個單詞列表,咱們將這個列表編碼成一個索引字符串 S 與一個索引列表 A。blog

例如,若是這個列表是 ["time", "me", "bell"],咱們就能夠將其表示爲 S = "time#bell#" 和 indexes = [0, 2, 5]。

對於每個索引,咱們能夠經過從字符串 S 中索引的位置開始讀取字符串,直到 "#" 結束,來恢復咱們以前的單詞列表。

那麼成功對給定單詞列表進行編碼的最小字符串長度是多少呢?

示例

輸入:words=["time","me","bell"]
輸出:10
說明:S="time#bell#",indexes=[0,2,5]

提示

  • 1<=words.length<=2000
  • 1<=word[i].length<=7
  • 每一個單詞都是小寫字母

思路
這個題目讀上去挺拗口的,但意思挺簡單的,就是若是一個單詞能夠表示爲另外一個單詞的後綴
便可當作一個單詞,例如:"time"和"me",me爲time的後綴,因此它們能夠看出一個單詞time。至此咱們能夠與字典樹聯繫起來了,但有人可能會說這是後綴類型的,而字典樹是判斷前綴的,其實咱們只需將單詞逆序插入就好了。

好比題目中的 ["time","me","bell"] 的逆序就是 ["emit","em","lleb"] 。能夠發現ememit的前綴。可是咱們必須先插入單詞長的字符串,不然就會產生問題,這裏能夠閱讀代碼理解。因此咱們插入以前須要根據單詞的長度由長到短排序。

代碼

class Solution {
    public int minimumLengthEncoding(String[] words) {
        int len = 0;
        Trie trie = new Trie();
        // 先對單詞列表根據單詞長度由長到短排序
        Arrays.sort(words, (s1, s2) -> s2.length() - s1.length());
        // 單詞插入trie,返回該單詞增長的編碼長度
        for (String word: words) {
            len += trie.insert(word);
        }
        return len;
    }
}

// 定義tire
class Trie {
    
    TrieNode root;
    
    public Trie() {
        root = new TrieNode();
    }

    public int insert(String word) {
        TrieNode cur = root;
        boolean isNew = false;
        // 倒着插入單詞
        for (int i = word.length() - 1; i >= 0; i--) {
            int c = word.charAt(i) - 'a';
            if (cur.children[c] == null) {
                isNew = true; // 是新單詞
                cur.children[c] = new TrieNode();
            }
            cur = cur.children[c];
        }
        // 若是是新單詞的話編碼長度增長新單詞的長度+1,不然不變。
        //若是是短單詞先插入的話,會計算短單詞的長度,後面長單詞也會計算,然而短單詞是 
       //長單詞的後綴,應捨去
        return isNew? word.length() + 1: 0;
    }
}

class TrieNode {
    char val;
    TrieNode[] children = new TrieNode[26];

    public TrieNode() {}
}

題解參考

總結:

至此咱們已經對字典樹有了初步理解,通常遇到須要大量判斷一個字符串是否是給點單詞列表中的前綴或後綴,能夠往字典樹方向思考,相比於用 HashMap,節省了大量運行時間和存儲空間,HashMap的效率取決於哈希函數的好壞,若一個壞的哈希函數致使了不少衝突,效率不必定比Trie樹高。

相關文章
相關標籤/搜索