【資料結構與演算法】Trie(字首樹)模板和例題

gonghr發表於2022-03-17

Trie 樹的模板

Trie 樹的簡介

Trie樹,又稱字典樹,單詞查詢樹或者字首樹,是一種用於快速檢索的多叉樹結構,如英文字母的字典樹是一個26叉樹,數字的字典樹是一個10叉樹。他的核心思想是空間換時間,空間消耗大但是插入和查詢有著很優秀的時間複雜度。

Trie 樹的定義

Trie樹的鍵不是直接儲存在節點中,而是由節點在樹中的位置決定。一個節點的所有子孫都有相同的字首(prefix),從根節點到當前結點的路徑上的所有字母組成當前位置的字串,結點可以儲存當前字串、出現次數、指標陣列(指向子樹)以及是否是結尾標誌等等。

? 簡圖

實際上每個節點有一個end屬性和一個字典長度的節點陣列

Trie 樹的實現

LeetCode 208. 實現 Trie (字首樹)

Trie(發音類似 "try")或者說 字首樹 是一種樹形資料結構,用於高效地儲存和檢索字串資料集中的鍵。這一資料結構有相當多的應用情景,例如自動補完和拼寫檢查。

請你實現 Trie 類:

Trie() 初始化字首樹物件。
void insert(String word) 向字首樹中插入字串 word 。
boolean search(String word) 如果字串 word 在字首樹中,返回 true(即,在檢索之前已經插入);否則,返回 false 。
boolean startsWith(String prefix) 如果之前已經插入的字串 word 的字首之一為 prefix ,返回 true ;否則,返回 false 。
 

示例:

輸入
["Trie", "insert", "search", "search", "startsWith", "insert", "search"]
[[], ["apple"], ["apple"], ["app"], ["app"], ["app"], ["app"]]
輸出
[null, null, true, false, true, null, true]

解釋
Trie trie = new Trie();
trie.insert("apple");
trie.search("apple");   // 返回 True
trie.search("app");     // 返回 False
trie.startsWith("app"); // 返回 True
trie.insert("app");
trie.search("app");     // 返回 True
 

提示:

1 <= word.length, prefix.length <= 2000
word 和 prefix 僅由小寫英文字母組成
insert、search 和 startsWith 呼叫次數 總計 不超過 3 * 104 次
class Trie {
    class TrieNode {
        boolean end;
        TrieNode[] tns = new TrieNode[26];
    }
    TrieNode root;
    public Trie() {
        root = new TrieNode();
    }
    
    public void insert(String word) {
        TrieNode p = root;
        for(int i = 0; i < word.length(); i++) {
            int u = word.charAt(i) - 'a';
            if(p.tns[u] == null) p.tns[u] = new TrieNode();
            p = p.tns[u];
        }
        p.end = true;
    }
    
    public boolean search(String word) {
        TrieNode p = root;
        for(int i = 0; i < word.length(); i++) {
            int u = word.charAt(i) - 'a';
            if(p.tns[u] == null) return false;
            p = p.tns[u];
        }
        return p.end;
    }
    
    public boolean startsWith(String prefix) {
        TrieNode p = root;
        for(int i = 0; i < prefix.length(); i++) {
            int u = prefix.charAt(i) - 'a';
            if(p.tns[u] == null) return false;
            p = p.tns[u];
        }
        return true;
    }
}

Trie 樹的例題

LeetCode 211. 新增與搜尋單詞

LeetCode 211. 新增與搜尋單詞

請你設計一個資料結構,支援 新增新單詞 和 查詢字串是否與任何先前新增的字串匹配 。

實現詞典類 WordDictionary :

WordDictionary() 初始化詞典物件
void addWord(word) 將 word 新增到資料結構中,之後可以對它進行匹配
bool search(word) 如果資料結構中存在字串與 word 匹配,則返回 true ;否則,返回  false 。word 中可能包含一些 '.' ,每個 . 都可以表示任何一個字母。
 

示例:

輸入:
["WordDictionary","addWord","addWord","addWord","search","search","search","search"]
[[],["bad"],["dad"],["mad"],["pad"],["bad"],[".ad"],["b.."]]
輸出:
[null,null,null,null,false,true,true,true]

解釋:
WordDictionary wordDictionary = new WordDictionary();
wordDictionary.addWord("bad");
wordDictionary.addWord("dad");
wordDictionary.addWord("mad");
wordDictionary.search("pad"); // return False
wordDictionary.search("bad"); // return True
wordDictionary.search(".ad"); // return True
wordDictionary.search("b.."); // return True
 

提示:

1 <= word.length <= 500
addWord 中的 word 由小寫英文字母組成
search 中的 word 由 '.' 或小寫英文字母組成
最多呼叫 50000 次 addWord 和 search
class WordDictionary {
    class Node {
        boolean end;
        Node[] tns = new Node[26];
    }
    Node root;
    public void insert(String s) {
        Node p = root;
        for(int i = 0; i < s.length(); i++) {
            int u = s.charAt(i) - 'a';
            if(p.tns[u] == null) p.tns[u] = new Node();
            p = p.tns[u];
        }
        p.end = true;
    }
    public WordDictionary() {
        root = new Node();
    }
    
    public void addWord(String word) {
        insert(word);
    }
    
    public boolean search(String s) {
        return dfs(s, root, 0);
    }
    public boolean dfs(String s, Node p, int idx) {
        int n = s.length();
        if(idx == n) return p.end;
        char c = s.charAt(idx);
        if(c == '.') {
            for(int i = 0; i < 26; i++) {
                if(p.tns[i] != null && dfs(s, p.tns[i], idx + 1)) return true;
            }
            return false;
        }
        else {
            int u = c - 'a';
            if(p.tns[u] == null) return false;
            return dfs(s, p.tns[u], idx + 1);
        }
    }
}

LeetCode 720. 詞典中最長的單詞

給出一個字串陣列 words 組成的一本英語詞典。返回 words 中最長的一個單詞,該單詞是由 words 詞典中其他單詞逐步新增一個字母組成。

若其中有多個可行的答案,則返回答案中字典序最小的單詞。若無答案,則返回空字串。

 

示例 1:

輸入:words = ["w","wo","wor","worl", "world"]
輸出:"world"
解釋: 單詞"world"可由"w", "wo", "wor", 和 "worl"逐步新增一個字母組成。
示例 2:

輸入:words = ["a", "banana", "app", "appl", "ap", "apply", "apple"]
輸出:"apple"
解釋:"apply" 和 "apple" 都能由詞典中的單片語成。但是 "apple" 的字典序小於 "apply" 
 

提示:

1 <= words.length <= 1000
1 <= words[i].length <= 30
所有輸入的字串 words[i] 都只包含小寫字母。
class Solution {
    class Trie {
        class TrieNode {
            boolean end;
            TrieNode[] tns = new TrieNode[26];
        }
        TrieNode root;
        public Trie() {
            root = new TrieNode();
        }
        public void insert(String s) {
            TrieNode p = root;
            for(int i = 0; i < s.length(); i++) {
                int u = s.charAt(i) - 'a';
                if(p.tns[u] == null) {
                    p.tns[u] = new TrieNode();
                }    
                p = p.tns[u];
            }
            p.end = true;
        }
        public boolean search(String s) {
            TrieNode p = root;
            for(int i = 0; i < s.length(); i++) {
                int u = s.charAt(i) - 'a';
                if(p.tns[u] == null) return false;
                p = p.tns[u];
            }
            return p.end;
        }
        public boolean startsWith(String s) {
            TrieNode p = root;
            for(int i = 0; i < s.length(); i++) {
                int u = s.charAt(i) - 'a';
                if(p.tns[u] == null) return false;
                p = p.tns[u];
            }
            return true;
        }
        public boolean query(String s) {
            TrieNode p = root;
            for(int i = 0; i < s.length(); i++) {
                int u = s.charAt(i) - 'a';
                if(p.tns[u] == null) return false;
                if(p.tns[u].end == false) return false;
                p = p.tns[u];
            }
            return true;
        }
    }
    public String longestWord(String[] words) {
        Trie t = new Trie();
        for(String word : words) {
            t.insert(word);
        }
        String ans = "";
        for(String word : words) {
            int lena = ans.length();
            int lenb = word.length();
            if(lenb < lena) continue;
            if(lenb == lena && word.compareTo(ans) > 0) continue;
            if(t.query(word)) ans = word;
        }
        return ans;
    }
}

相關文章