1. 程式人生 > 實用技巧 >LeetCode208. 實現 Trie (字首樹)

LeetCode208. 實現 Trie (字首樹)

方法一

可以直接用一個數組trie存放所有插入的字串,然後insert和startsWith操作只需要遍歷trie陣列,逐個判斷即可。

程式碼:

class Trie {
public:
    vector<string> trie;
    /** Initialize your data structure here. */
    Trie() {

    }
    
    /** Inserts a word into the trie. */
    void insert(string word) {
        trie.push_back(word);
    }
    
    /** Returns if the word is in the trie. */
    bool search(string word) {
        for(const auto &s : trie) {
            if(s == word) {
                return true;
            }
        }
        return false;
    }
    
    /** Returns if there is any word in the trie that starts with the given prefix. */
    bool startsWith(string prefix) {
        int size = prefix.size();
        for(const auto &s : trie) {
            if(s.size() >= size && s.substr(0, size) == prefix) {
                return true;
            }
        }
        return false;
    }
};

/**
 * Your Trie object will be instantiated and called as such:
 * Trie* obj = new Trie();
 * obj->insert(word);
 * bool param_2 = obj->search(word);
 * bool param_3 = obj->startsWith(prefix);
 */

方法二

方法一併不是嚴格的trie樹。

我們可以用一個結構體存放trie樹的每個節點,每個節點最多可能有a~z共26個孩子,為了方便,我們都用整數表示;另外每個節點還需要記錄它們是否是一個單詞的結尾,
可以用一個布林變數is_end表示。

插入的時候,從根節點(空)開始,看當前節點對應的孩子是否和現在這個字母匹配,如果匹配,就繼續往下走,如果不匹配,就插入一個字母,實際上,這就是trie樹。

搜尋的時候同理,如果當前節點對應的孩子和現在查詢的字母相同,就繼續往下走,否則返回false,如果最後存在一條路徑,從根節點到某個節點上的字母組合成了查詢的字串,
還需要看看結尾的字母的is_end變數是否為true,只有當路徑存在且路徑最後一個字母的is_end變數為true時表示搜尋成功。

startsWith和搜尋類似,只不過我們只需要找到路徑即可,即使路徑最後一個節點的is_end變數不為true也沒關係,只要存在路徑,就說明有單詞是以字串prefix開頭的,返回true。

class Trie {
public:
    struct Node {
        Node* son[26];            //節點最多有26個孩子
        bool is_end;              //如果節點是某個單詞的結尾,標記一下
    }*root;
    /** Initialize your data structure here. */
    Trie() {
        root = new Node();
    }
    
    /** Inserts a word into the trie. */
    void insert(string word) {
        Node* p = root;                             //p表示我們當前是在哪個節點,最開始在根節點(空)
        for(int i = 0; i < word.size(); ++i) {      //從根節點開始逐層往下搜尋
            int u = word[i] - 'a';
            if(p -> son[u] == NULL) {              //如果某節點不存在,則新建一個節點
                p -> son[u] = new Node();
            }
            p = p -> son[u];                        //繼續往下走
        }
        p -> is_end = true;                         //標記一下這是一個單詞的結尾
    }
    
    /** Returns if the word is in the trie. */
    bool search(string word) {
        Node* p = root;
        for(int i = 0; i < word.size(); ++i) {
            int u = word[i] - 'a';
            if(p -> son[u] == NULL) {
                return false;                        //如果沒找到,返回false
            }
            p = p -> son[u];
        }
        return p -> is_end;                          //找到一條路徑上的單詞和word相同,還要看一下is_end變數是否為true,因為這可能是某個單詞的字首
    }
    
    /** Returns if there is any word in the trie that starts with the given prefix. */
    bool startsWith(string prefix) {                  //startsWith和search類似,唯一不同就是隻要路徑存在就返回true
        Node* p = root;
        for(int i = 0; i < prefix.size(); ++i) {
            int u = prefix[i] - 'a';
            if(p -> son[u] == NULL) {
                return false;
            }
            p = p -> son[u];
        }
        return true;
    }
};

/**
 * Your Trie object will be instantiated and called as such:
 * Trie* obj = new Trie();
 * obj->insert(word);
 * bool param_2 = obj->search(word);
 * bool param_3 = obj->startsWith(prefix);
 */