Trie

只要把数据结构想清楚了,其实 Trie 树很简单的。

摘自百度百科

字典树,又称单词查找树。利用字符串的公共前缀来减少查询时间。

典型应用是用于统计,排序和保存大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。

它有3个基本性质:

  • 根节点不包含字符,除根节点外每一个节点都只包含一个字符;
  • 从根节点到某一节点,路径上经过的字符连接起来,为该节点对应的字符串;
  • 每个节点的所有子节点包含的字符都不相同。

Implement Trie (Prefix Tree)

https://leetcode.com/problems/implement-trie-prefix-tree/

Implement a trie with insert, search, and startsWith methods.

Note: You may assume that all inputs are consist of lowercase letters a-z.

TrieNode 包含三个变量,当前结点所对应的字符,当前结点是否为单词(本题中,由于可以用位置代表当前字符,所以,该变量不是必须的,下面的实现中其实也没有用到),当前结点的孩子结点(用指针数组来表示,大小已定)。还可以考虑删除操作,以及重复插入的情况。

next 有不同的实现方式。

TrieNode* next[26]; // fast, Runtime: 56 ms
memset(next, 0, sizeof(TrieNode*)* 26); // initialize
vector<TrieNode*> children; // middle, Runtime: 108 ms
children(vector<TrieNode*>(26, nullptr)) // initialize
unordered_map<char, TrieNode*> next; // slow, Runtime: 188 ms without deconstruct, 228 ms with deconstruct

Trie 记录树根,提供相关服务。search 和 startsWith 的大部分内容是一样的,故提取出 find 函数。

#include <string>
using std::string;

// Runtime: 64 ms with destructor
// Runtime: 56 ms without destructor

// https://leetcode.com/discuss/34832/two-c-o-n-solutions-array-or-hashtable

class TrieNode {
public:
    // Initialize your data structure here.
    TrieNode() : c(0), isWord(false) {
        memset(next, 0, sizeof(TrieNode*)* 26);
    }
    TrieNode(char _c) : c(_c), isWord(false) {
        memset(next, 0, sizeof(TrieNode*)* 26);
    }
    ~TrieNode() {
        delete []next;
    }
    TrieNode* next[26];
    char c;
    bool isWord;
};

class Trie {
public:
    Trie() {
        root = new TrieNode();
    }
    ~Trie() {
        destroy(root);
    }
    void destroy(TrieNode* node) {
        for (int i = 0; i < 26; i++) {
            if (node->next[i]) {
                destroy(node->next[i]);
            }
        }
        delete node;
    }
    // Inserts a word into the trie.
    void insert(string word) {
        TrieNode* p = root;
        for (auto ch : word) {
            int index = ch - 'a';
            if (!p->next[index]) {
                p->next[index] = new TrieNode(ch);
            }
            p = p->next[index];
        }
        p->isWord = true;
    }

    // Returns if the word is in the trie.
    bool search(string word) {
        TrieNode* p = find(word);
        return p && p->isWord;
    }

    // Returns if there is any word in the trie
    // that starts with the given prefix.
    bool startsWith(string prefix) {
        return find(prefix) != nullptr;
    }

private:
    TrieNode* find(string key) {
        TrieNode* p = root;
        for (auto ch : key) {
            p = p->next[ch - 'a'];
            if (!p) {
                break;
            }
        }
        return p;
    }
    TrieNode* root;
};

// Your Trie object will be instantiated and called as such:
// Trie trie;
// trie.insert("somestring");
// trie.search("key");

Add and Search Word - Data structure design

https://leetcode.com/problems/add-and-search-word-data-structure-design/

Design a data structure that supports the following two operations:

void addWord(word)
bool search(word)

search(word) can search a literal word or a regular expression string containing only letters a-z or .. A . means it can represent any one letter.

#include <string>
using std::string;
// Runtime: 76 ms
// we doesn't need a character c.
class TrieNode{
public:
    // Initialize your data structure here.
    TrieNode() : isWord(false) {
        memset(next, 0, sizeof(TrieNode*)* 26);
    }
    TrieNode(char _c) : c(_c), isWord(false) {
        memset(next, 0, sizeof(TrieNode*)* 26);
    }
    ~TrieNode() {
        delete []next;
    }
    TrieNode* next[26];
    char c;
    bool isWord;
};
class WordDictionary {
public:
    WordDictionary() {
        root = new TrieNode();
    }
    // Adds a word into the data structure.
    void addWord(string word) {
        TrieNode* p = root;
        for (auto ch : word) {
            int index = ch - 'a';
            if (!p->next[index]) {
            //if (p->next[index] == nullptr) {
                p->next[index] = new TrieNode(ch);
            }
            p = p->next[index];
        }
        p->isWord = true;
    }

    // Returns if the word is in the data structure. A word could
    // contain the dot character '.' to represent any one letter.
    bool search(string word) {
        return searchHelper(word.c_str(), root);
    }

private:
    //bool searchHelper(const char* word, TrieNode* node) {
    //  for (int i = 0; word[i]; i++) {
    //      if (node && word[i] != '.') {
    //          node = node->next[word[i] - 'a'];
    //      }
    //      else if (node && word[i] == '.') {
    //          for (int j = 0; j < 26; j++) {
    //              if (searchHelper(word + i + 1, node->next[j])) {
    //                  return true;
    //              }
    //          }
    //          // 'a', search(".a") or search("a.")
    //          return false;
    //      }
    //      else {
    //          break;
    //      }
    //  }
    //  return node && node->isWord;
    //}
    bool searchHelper(const char* word, TrieNode* node) {
        // Runtime: 80 ms
        if (!node) {
            return false;
        }
        if (*word == '\0') {
            return node->isWord;
        }
        else if (*word == '.') {
            for (int j = 0; j < 26; j++) {
                if (searchHelper(word + 1, node->next[j])) {
                    return true;
                }
            }
            return false;
        }
        else {
            return searchHelper(word + 1, node->next[*word - 'a']);
        }
    }
    TrieNode* root;
};

// Your WordDictionary object will be instantiated and called as such:
// WordDictionary wordDictionary;
// wordDictionary.addWord("word");
// wordDictionary.search("pattern");

Word Search II

https://leetcode.com/problems/word-search-ii/

Given a 2D board and a list of words from the dictionary, find all words in the board.

#include <string>
#include <vector>
#include <set>
#include <unordered_map>
using std::string;
using std::vector;
using std::set;
using std::unordered_map;
// Runtime: 52 ms void insert(string word, int i)
// Best: 48 ms  void insert(vector<string>& words, int i)
class TrieNode {
public:
    TrieNode() : isWord(false), isUsed(false), index(-1) {
        memset(next, 0, sizeof(TrieNode*)* 26);
    }
    TrieNode* next[26];
    bool isWord;
    bool isUsed;
    int index;
};

class Trie {
public:
    Trie() {
        root = new TrieNode();
    }
    void insert(string word, int i) {
        TrieNode* p = root;
        for (auto ch : word) {
            if (!p->next[ch - 'a']) {
                p->next[ch - 'a'] = new TrieNode();
            }
            p = p->next[ch - 'a'];
        }
        p->isWord = true;
        p->isUsed = false;
        p->index = i;
    }
    void insert(vector<string>& words, int i) {
        TrieNode* p = root;
        for (auto ch : words[i]) {
            if (!p->next[ch - 'a']) {
                p->next[ch - 'a'] = new TrieNode();
            }
            p = p->next[ch - 'a'];
        }
        p->isWord = true;
        p->isUsed = false;
        p->index = i;
    }
    void insert(vector<string>& words) {
        for (int i = 0; i < words.size(); i++) {
            // insert(words[i], i);
            insert(words, i);
        }
    }
    vector<string> findWords(vector<vector<char>>& board, vector<string>& words) {
        vector<string> ret;
        const int ROW = board.size();
        const int COL = board[0].size();
        for (int i = 0; i < ROW; i++) {
            for (int j = 0; j < COL; j++) {
                findWords(ret, words, root, board, i, j, ROW, COL);
            }
        }
        return ret;
    }
    void findWords(vector<string>& ret, vector<string>& words, TrieNode* node, vector<vector<char>>&board, int i, int j, const int Row, const int Col) {
        // doesn't need count, because node->next[index] will be nullptr.
        // 60 ms
        if (board[i][j] == '.') {
            return;
        }
        int index = board[i][j] - 'a';
        TrieNode* next = node->next[index];
        if (next) {
            char tmp = board[i][j];
            if (next->isWord && !next->isUsed) {
                ret.push_back(words[next->index]);
                next->isUsed = true;
            }
            board[i][j] = '.';
            if (i > 0) {
                findWords(ret, words, next, board, i - 1, j, Row, Col);
            }
            if (i + 1 < Row) {
                findWords(ret, words, next, board, i + 1, j, Row, Col);
            }
            if (j > 0) {
                findWords(ret, words, next, board, i, j - 1, Row, Col);
            }
            if (j + 1 < Col) {
                findWords(ret, words, next, board, i, j + 1, Row, Col);
            }
            board[i][j] = tmp;
        }
    }
private:
    TrieNode* root;
};

class Solution {
public:
    vector<string> findWords(vector<vector<char>>& board, vector<string>& words) {
        vector<string> ret;
        if (board.empty() || board[0].empty()) {
            return ret;
        }
        Trie trie;
        trie.insert(words);
        return trie.findWords(board, words);
    }
};

你可能感兴趣的:(LeetCode,C++,Trie-树)