题目:Implement Trie (Prefix Tree)

实现字典树。

什么是字典树?

Trie树,又叫字典树、前缀树(Prefix Tree)、单词查找树 或 键树,是一种多叉树结构。如下图:

上图是一棵Trie树,表示了关键字集合{“a”, “to”, “tea”, “ted”, “ten”, “i”, “in”, “inn”} 。从上图可以归纳出Trie树的基本性质:

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

通常在实现的时候,会在节点结构中设置一个标志,用来标记该结点处是否构成一个单词(关键字)。

可以看出,Trie树的关键字一般都是字符串,而且Trie树把每个关键字保存在一条路径上,而不是一个结点中。另外,两个有公共前缀的关键字,在Trie树中前缀部分的路径相同,所以Trie树又叫做前缀树(Prefix Tree)。

更详细的内容请参照:http://www.cnblogs.com/yeqluofwupheng/p/6793516.html

树的节点结构如下:

struct TrieNode{int count = 0;//该节点代表的单词的个数,由此判断当前节点是否构成一个单词vector<TrieNode*> children;//26个小写字母,以它们的值为下标};

上面count用来记录访问次数,同时也可以标志从根节点到当前节点的字符串是否为一个单词。

第一次插入的时候讲最后一个字母对应的树节点的count设为1.

下面包括void insert(string word);bool search(string word);bool startsWith(string prefix);三个操作。

class Trie {
public:/** Initialize your data structure here. */Trie() {root = new TrieNode();root->count = 0;for (size_t i = 0; i < 26; i++)    {root->children.push_back(nullptr);}}/** Inserts a word into the trie. */void insert(string word) {auto it = word.cbegin();auto p = root;while (it != word.cend()){auto pos = *it - 'a';if (!p->children.at(pos)){//创建字典树节点TrieNode* newChild = new TrieNode();newChild->count = 0;for (size_t i = 0; i < 26; i++)    {newChild->children.push_back(nullptr);}p->children.at(pos) = newChild;//连接
            }p = p->children.at(pos);++it;}++(p->count);//增加他的访问,p是最后的节点
    }/** Returns if the word is in the trie. */bool search(string word) {auto it = word.cbegin();auto p = root;while (it != word.cend()){//正常退出表示找到了该单词的末尾位置auto pos = *it - 'a';if (!p->children.at(pos))return false;//不能构成单词p = p->children.at(pos);++it;}if (p->count)return true;//count>0表示当前节点构成一个单词return false;}/** Returns if there is any word in the trie that starts with the given prefix. */bool startsWith(string prefix) {auto it = prefix.cbegin();auto p = root;while (it != prefix.cend()){//正常退出表示找到了该单词的末尾位置auto pos = *it - 'a';if (!p->children.at(pos))return false;//不能构成单词p = p->children.at(pos);++it;}return true;}
private:struct TrieNode{int count = 0;//该节点代表的单词的个数,由此判断当前节点是否构成一个单词vector<TrieNode*> children;//26个小写字母,以它们的值为下标
    };TrieNode* root;
};

题目:Add and Search Word - Data structure design

字典树的简单应用,在查找上增加了'.'的正则表达式;注意题目中只有一个'.',而没有省略号,不要搞错了。

基本思想就是使用字典树,树节点结构,创建,添加和上面都是一样的。主要在查找上有一点区别。

class WordDictionary {
public:/** Initialize your data structure here. */WordDictionary() {dict = new TrieNode();dict->isKey = false;for (size_t i = 0; i < 26; i++)    {dict->children.push_back(nullptr);}}/** Adds a word into the data structure. */void addWord(string word) {auto it = word.cbegin();auto p = dict;while (it != word.cend()){auto pos = *it - 'a';if (!p->children.at(pos)){TrieNode* newChild = new TrieNode();newChild->isKey = false;for (size_t i = 0; i < 26; i++)    {newChild->children.push_back(nullptr);}p->children.at(pos) = newChild;}p = p->children.at(pos);++it;}p->isKey = 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 query(word, dict);}
private:struct TrieNode{bool isKey = false;//标记从树根到断当前节点是否构成一个单词vector<TrieNode*> children;//26个小写字母,以它们的值为下标
    };TrieNode* dict;bool query(string word, TrieNode* root){if (!root)return false;auto p = root;for (size_t i = 0; i < word.size(); ++i){//正常退出表示找到了该单词的末尾位置if (word[i] == '.'){//一个点string sub = i == word.size() - 1 ? word.substr(i, 0) : word.substr(i + 1, word.size() - 1 - i);for (size_t i = 0; i < p->children.size(); i++){if (query(sub, p->children.at(i)))return true;//找到符合的单词
                }return false;}auto pos = word[i] - 'a';if (!p->children.at(pos))return false;//不能构成单词p = p->children.at(pos);}if (p->isKey)return true;//count>0表示当前节点构成一个单词return false;}
};

bool search(string word)需要使用递归实现,所以有了query()的实现。主要就是当遇到'.'的时候循环递归判断当前树节点的每一个子节点。

注意:

1.空串的情况:string sub = i == word.size() - 1 ? word.substr(i, 0) : word.substr(i + 1, word.size() - 1 - i);此时单词的最后一个字符是'.',substr中不能传i+1;

2.空节点的情况:if (!root)return false;

题目:Word Search II

给定一个字符数组和一组单词,将单词的每个字母在字符数组中邻接(单词前一个字母和后一个字母是在前后左右某一个方向上相邻)的单词输出。

例如

Given words = ["oath","pea","eat","rain"] and board =

[['o','a','a','n'],['e','t','a','e'],['i','h','k','r'],['i','f','l','v']
]

Return ["eat","oath"].

思路:

使用Trie树,将单词放入空的Trie树中,然后遍历字符数组,递归查找,每当找到Trie树的单词节点(可以组成一个单词的节点),就将单词放入数组中,最后返回该数组即可。

实现如下:

Trie树的构造方式:

class TrieNode{
public:int count;//该节点代表的单词的个数,由此判断当前节点是否构成一个单词vector<TrieNode*> children;//26个小写字母,以它们的值为下标TrieNode() :count(0){children = vector<TrieNode*>(26, nullptr);}
};class Trie {
public:/** Initialize your data structure here. */Trie() {root = new TrieNode();}Trie(vector<string>& words) {root = new TrieNode();for (size_t i = 0; i < words.size(); ++i)    {insert(words.at(i));}}TrieNode* getRoot(){ return root; }/** Inserts a word into the trie. */void insert(string word) {auto it = word.cbegin();auto p = root;while (it != word.cend()){auto pos = *it - 'a';if (!p->children.at(pos)){//创建字典树节点TrieNode* newChild = new TrieNode();newChild->count = 0;for (size_t i = 0; i < 26; i++)    {newChild->children.push_back(nullptr);}p->children.at(pos) = newChild;//连接
            }p = p->children.at(pos);++it;}p->count = 1;//增加他的访问,p是最后的节点
    }/** Returns if the word is in the trie. */bool search(string word) {auto it = word.cbegin();auto p = root;while (it != word.cend()){//正常退出表示找到了该单词的末尾位置auto pos = *it - 'a';if (!p->children.at(pos))return false;//不能构成单词p = p->children.at(pos);++it;}if (p->count)return true;//count>0表示当前节点构成一个单词return false;}/** Returns if there is any word in the trie that starts with the given prefix. */bool startsWith(string prefix) {auto it = prefix.cbegin();auto p = root;while (it != prefix.cend()){//正常退出表示找到了该单词的末尾位置auto pos = *it - 'a';if (!p->children.at(pos))return false;//不能构成单词p = p->children.at(pos);++it;}return true;}
private:friend class TrieNode;//不能丢,否则会报“不能将TrieNode*类型的值分配到TrieNode*类型的实体”的错误TrieNode* root;
};

这里完全使用C++的语法实现,注意Trie中friend class TrieNode;不能少;

其他和上面类似,不再赘述。

vector<string> LeetCode::findWords(vector<vector<char>>& board, vector<string>& words){Trie tree(words);set<string> result;//防止添加重复的单词for (size_t i = 0; i < board.size(); ++i){for (size_t j = 0; j < board.at(0).size(); ++j){findWords(board, i, j, tree.getRoot(), string(""), result);}}vector<string> strs;for each (auto s in result){strs.push_back(s);}return strs;
}void LeetCode::findWords(vector<vector<char>>& board, int i, int j, TrieNode* root, string& word, set<string>& result){if (i < 0 || i >= board.size() || j < 0 || j >= board.at(0).size() || board.at(i).at(j) == ' ')return;//越界返回TrieNode* p = root->children.at(board.at(i).at(j) - 'a');if (p){word += board.at(i).at(j);if (p->count)result.insert(word);//找到一个单词char ch = board.at(i).at(j);board.at(i).at(j) = ' ';//一个字符不能重复使用findWords(board, i - 1, j, p, word, result);//左边findWords(board, i, j - 1, p, word, result);//下边findWords(board, i + 1, j, p, word, result);//右边findWords(board, i, j + 1, p, word, result);//上边board.at(i).at(j) = ch;word.pop_back();}
}

注意:

1.单词的回溯

word.pop_back();

2.避免字符数组中字符的重复使用

board.at(i).at(j) = ' ';//一个字符不能重复使用

上面为了避免重复的单词,使用了set容器,实际上因为有Trie树中count字段代表频度,可以使用它来避免重复单词。

vector<string> findWords(vector<vector<char>>& board, vector<string>& words) {Trie tree(words);vector<string> result;//防止添加重复的单词string word;for (size_t i = 0; i < board.size(); ++i){for (size_t j = 0; j < board.at(0).size(); ++j){findWords(board, i, j, tree.getRoot(), word, result);}}return result;}void findWords(vector<vector<char>>& board, int i, int j, TrieNode* root, string& word, vector<string>& result){if (i < 0 || i >= board.size() || j < 0 || j >= board.at(0).size() || board.at(i).at(j) == ' ')return;//越界返回TrieNode* p = root->children.at(board.at(i).at(j) - 'a');if (p){word += board.at(i).at(j);if (p->count == 1){//初次(除了创建树的时候)访问时,添加result.push_back(word);//找到一个单词p->count += 1;//访问过一次频度加一
            }char ch = board.at(i).at(j);board.at(i).at(j) = ' ';//一个字符不能重复使用findWords(board, i - 1, j, p, word, result);//左边findWords(board, i, j - 1, p, word, result);//下边findWords(board, i + 1, j, p, word, result);//右边findWords(board, i, j + 1, p, word, result);//上边board.at(i).at(j) = ch;word.pop_back();}}

转载于:https://www.cnblogs.com/yeqluofwupheng/p/6793195.html

[LeetCode]Implement Trie (Prefix Tree)相关推荐

  1. LeetCode Implement Trie (Prefix Tree)(字典树)

    问题:实现具有insert,search,startsWith方法的字典树 思路:就是要实现Trie数据结构 具体代码参考: https://github.com/wuli2496/OJ/tree/m ...

  2. LeetCode Implement Trie(Prefix Tree)

    131231231 转载于:https://www.cnblogs.com/ZHONGZHENHUA/p/10905337.html

  3. 208. Implement Trie (Prefix Tree)(Leetcode每日一题-2021.04.14)

    Problem A trie (pronounced as "try") or prefix tree is a tree data structure used to effic ...

  4. LeetCode 208. Implement Trie (Prefix Tree)

    Implement a trie with insert, search, and startsWith methods. Note: You may assume that all inputs a ...

  5. leetcode 208. Implement Trie (Prefix Tree) | 208. 实现 Trie 前缀树(Java)

    题目 https://leetcode.com/problems/implement-trie-prefix-tree/ 题解 第一版:暴力法 import java.util.LinkedHashS ...

  6. Leetcode 208.实现 Trie (前缀树)(Implement Trie (Prefix Tree))

    Leetcode 208.实现 Trie (前缀树) 1 题目描述(Leetcode题目链接)   实现一个 Trie (前缀树),包含 insert, search, 和 startsWith 这三 ...

  7. 208. Implement Trie (Prefix Tree)

    题目: Implement a trie with insert, search, and startsWith methods. 链接: http://leetcode.com/problems/i ...

  8. LeetCode-208 Implement Trie (Prefix Tree)

    题目描述 Implement a trie with insert, search, and startsWith methods. 题目大意 实现对一棵树的插入.搜索以及前序查找操作. (树的每个节 ...

  9. Trie(前缀树,prefix tree)

    trie或者prefix tree(前缀树),是一种有序树数据结构,它通常被存储在一个以字符串为关键字的联合数组中.于二叉搜索树不同,在树里没有结点存储与结点相关联的关键字.它是用它在树中的位置展示与 ...

最新文章

  1. 二叉树节点间的最大距离
  2. HTML5 WebSockets学习
  3. 2.6内核的配置与编译
  4. 缓存淘汰算法 (http://flychao88.iteye.com/blog/1977653)
  5. 【译】Effective TensorFlow Chapter10——在TensorFlow中利用多GPU处理并行数据
  6. Linux-Android启动之Init进程前传
  7. python网络爬虫与信息提取 学习笔记day3
  8. 【2016年第6期】21世纪天文学面临的大数据和研究范式转型
  9. python 线程的使用
  10. 干掉项目中杂乱的 if-else,试试状态模式,这才是优雅的实现方式!
  11. Amanda之安装、部署、测试以及优缺点
  12. 谁负责本单位的各部门计算机,关于加强计算机校园网管理的若干认识
  13. 计算机组成原理白中英第五版之指令系统
  14. 中国移动彩信业务资料集合
  15. SQL Sever——远程过程调用失败(0x800706be)
  16. 图像同态滤波的Butterworth方程(Butterworth equations for homomorphic Filtering of images)
  17. linux ubuntu实验
  18. 判断apk是否加固或混淆,Python + dex2jar-2.0实现方法
  19. 【pytest-html】深度探索pytest-html测试报告的自定义使用
  20. OUC2021秋-软件工程-期末(回忆版)

热门文章

  1. MySQL Cluster(MySQL 集群) 初试(转)
  2. 使用P3P共享Cookie与Session小结
  3. 选美大赛示例 你会选谁
  4. CentOS下的Memcache安装
  5. “柔”,“软”,“微”,“弱”何以成为互联网时代的时髦词汇
  6. ES6中的class是如何实现的?(附Babel编译的ES5代码详解)
  7. Flutter瘦身大作战
  8. 转mosquitto auth plugin 编译配置
  9. Kafka实现细节(下)
  10. Android按钮事件的4种写法