[翻譯]數據結構——trie樹介紹

1、開篇說明

1.本文原文來自於leetcode上的算法題Implement Trie的解決方案.
2.原文地址
3.新手獻醜了,但願你們輕拍~(微笑)java


2、原文在這

1.問題描述

算法題:
經過編寫插入、查詢、判斷開頭等方法完成一個trie樹。node

例子:算法

Trie trie = new Trie();
trie.insert("apple");
trie.search("apple");   // returns true
trie.search("app");     // returns false
trie.startsWith("app"); // returns true
trie.insert("app");   
複製代碼

提示:數組

  • 你能夠假設全部的輸入都是由小寫字母a-z組成的。
  • 全部的輸入string數組都不爲空。

總結:
這篇文章是寫給中等水平的讀者的,將會介紹數據結構trie(前綴樹)和其中的常見操做。數據結構

2.解決方法:

2.1應用:
trie(前綴樹)是一種樹形數據結構,經常用來在字符串的數據集中檢索一個關鍵詞。目前,trie數據結構已經被高效地應用在了不少領域:
(1)自動填充
app

谷歌實時的關鍵詞推薦
圖片1.谷歌實時的關鍵詞推薦

(2)拼寫檢查
圖片2.在文字處理機中的拼寫檢查

(3)IP路由(最長的路由匹配)
圖片3.最長路由匹配算法
(4)九鍵輸入法的預測文字
圖片4.九鍵輸入法在1990年代應用在手機中用來輸入文字
(5)完成文字遊戲
圖片5.經過減小搜索空間,trie可以很好的完成boggle這個遊戲

有不少其餘的數據結構如,平衡樹,hash表都可以在一個string的數據集中查找單詞,可是咱們爲何要使用trie呢?雖然hash表對於找到一個關鍵詞(key)只須要 O (1)的時間複雜度,可是在下列操做中,它就表現得不是很高效了。

  • 找到擁有共同前綴的全部關鍵詞(key)。
  • 根據字典序枚舉全部字符串

trie優於hash表的另一個緣由是,但hash表的數據規模擴大後,將會出現不少的hash碰撞,所以查詢時間複雜度將會提升到O (n),n 是插入的關鍵詞的個數。而相比於hash表,trie在存儲不少具備共同前綴的關鍵詞時須要的空間更少。在這個例子裏trie只須要O (m)的時間複雜度(m 是關鍵詞的長度)。而在平衡樹裏查詢一個關鍵詞,則須要花費O (m logn)
2.2 trie節點結構
trie是一個有根樹,它的節點有如下幾個字段:spa

  • 與子節點間最多有R個鏈接,每一個鏈接對應到R個字母中的一個。這個R個字母來自於字母表。在這篇文章中,咱們假設R是26,即26個小寫的拉丁字母。
  • 一個布爾值isEnd,說明該布爾值說明當前節點是不是一個關鍵詞的結尾,不然就只是該關鍵詞的前綴。
    圖6.表明關鍵字「leet」在trie中的表達

    java編寫的trie節點
class TrieNode {

    // R links to node children
    private TrieNode[] links;

    private final int R = 26;

    private boolean isEnd;

    public TrieNode() {
        links = new TrieNode[R];
    }

    public boolean containsKey(char ch) {
        return links[ch -'a'] != null;
    }
    public TrieNode get(char ch) {
        return links[ch -'a'];
    }
    public void put(char ch, TrieNode node) {
        links[ch -'a'] = node;
    }
    public void setEnd() {
        isEnd = true;
    }
    public boolean isEnd() {
        return isEnd;
    }
}
複製代碼

2.3 trie中最多見的操做——添加和查詢關鍵詞
(1)添加關鍵詞到trie中
咱們經過遍歷trie來插入關鍵詞。咱們從根節點開始,搜尋和關鍵詞第一個字母對應的鏈接,這裏通常有兩種狀況:設計

  • 若是鏈接存在,那麼咱們就順着這個鏈接往下移到下一子層,接着搜尋關鍵詞的下一個字母對應的鏈接。
  • 若是鏈接不存在,那麼咱們就新建一個trie節點,對應着如今的關鍵詞字母,創建與父節點的鏈接。

咱們重複這個步驟,直處處理完關鍵詞的最後一個字母,而後標記最後的節點爲結束節點。算法結束。 code

插入關鍵字到trie中

java編寫的插入方法

class Trie {
    private TrieNode root;

    public Trie() {
        root = new TrieNode();
    }

    // Inserts a word into the trie.
    public void insert(String word) {
        TrieNode node = root;
        for (int i = 0; i < word.length(); i++) {
            char currentChar = word.charAt(i);
            if (!node.containsKey(currentChar)) {
                node.put(currentChar, new TrieNode());
            }
            node = node.get(currentChar);
        }
        node.setEnd();
    }
}
複製代碼

複雜度分析:cdn

  • 時間複雜度O(m),m 是關鍵詞的長度。在算法的每一次循環中,咱們要麼檢查節點要麼新建一個節點,直到該關鍵詞的最後一個字母。因此,這隻須要進行m次操做。
  • 空間複雜度O(m)。最糟糕的狀況是,新插入的關鍵詞和已經存在trie的關鍵詞沒有共同的前綴,所以咱們必須插入m個新的節點,所以須要O(m)空間複雜度。

(2)在trie中搜索關鍵詞
每個關鍵詞在trie中均可以被一條從根節點到子節點的路徑所表示。咱們將根據關鍵詞的第一個字母從根節點開始搜索,而後檢查節點上的每個鏈接對應的字母,通常有兩種狀況:

  • 存在對應關鍵詞字母的鏈接,咱們將從該鏈接移動到下一個節點,而後搜索關鍵詞的下一個字母對應的鏈接。
  • 對應的鏈接不存在,若是此時已經遍歷到了關鍵詞的最後一個字母,則把當前的節點標記爲結束節點,而後返回true。固然還有另外兩種狀況,咱們會返回false:
    • 關鍵詞的字母沒有遍歷完,但沒有辦法接着在trie中找到根據關鍵詞字母的造成的路徑,因此trie中不存在該關鍵詞。
    • 關鍵詞的字母的已經遍歷完了,但當前的節點不是結束節點,所以搜索的關鍵詞只是trie中的某一個關鍵字的前綴。

圖8.在trie中搜索某個關鍵詞
java編寫搜索關鍵詞的方法

class Trie {
    ...

    // search a prefix or whole key in trie and
    // returns the node where search ends
    private TrieNode searchPrefix(String word) {
        TrieNode node = root;
        for (int i = 0; i < word.length(); i++) {
           char curLetter = word.charAt(i);
           if (node.containsKey(curLetter)) {
               node = node.get(curLetter);
           } else {
               return null;
           }
        }
        return node;
    }

    // Returns if the word is in the trie.
    public boolean search(String word) {
       TrieNode node = searchPrefix(word);
       return node != null && node.isEnd();
    }
}
複製代碼

複雜度分析:

  • 時間複雜度:O(m)。在算法的每一步都是搜索關鍵詞的下一個字母,所以在最差的狀況下,算法須要執行m步。
  • 空間複雜度:O(1)。

(3)在trie中搜索關鍵詞的前綴

這個方法和咱們在trie中用來搜索關鍵詞的方法很相似。咱們從根節點開始移動,直到關鍵詞前綴的每一個字母都被搜索到了,或者,沒有辦法在trie中根據關鍵詞的當前字母找到接下去的路徑。這個方法和前面提到的搜索關鍵詞的惟一不一樣在於,當咱們遍歷到關鍵詞前綴的最後一個字母時,咱們老是返回true,咱們不須要考慮當前的節點是否有結束標誌,由於咱們只是搜索關鍵詞的前綴,而不是整個關鍵詞。

圖9.在trie中搜索關鍵詞前綴

java編寫的搜索關鍵詞前綴的方法

class Trie {
    ...

    // Returns if there is any word in the trie
    // that starts with the given prefix.
    public boolean startsWith(String prefix) {
        TrieNode node = searchPrefix(prefix);
        return node != null;
    }
}
複製代碼

複雜度分析:

  • 時間複雜度:O(m)
  • 空間複雜度:O(1)

3. 應用問題
這裏有一些很是合適應你們去練習的問題,這些問題都能用trie數據結構解決。

這篇分析出自@elmirap

相關文章
相關標籤/搜索