前缀树
前缀树的几种操作:
- 建树/插入
- 查找单词是否在树中
- 判断是否以某一前缀开头(StartsWith)
TrieNode需要的成员:
- bool IsWord,记录从root到当前节点的路径是否组成单词
- TrieNode[] Children / Dictionary<char, TrieNode>,孩子节点们
- 该node对应的一些值什么的(可选,看题目)
1. 前缀树的实现
用数组实现
public class Trie
{
class TrieNode
{
public bool IsWord { get; set; }
public TrieNode[] Children = new TrieNode[26];
}
private TrieNode root = new TrieNode();
/** Initialize your data structure here. */
public Trie()
{ }
/** Inserts a word into the trie. */
public void Insert(string word)
{
var cur = root;
foreach (var c in word)
{
if (cur.Children[c - 'a'] == null)
{
cur.Children[c - 'a'] = new TrieNode();
}
cur = cur.Children[c - 'a'];
}
cur.IsWord = true;
}
/** Returns if the word is in the trie. */
public bool Search(string word)
{
var cur = root;
foreach (var c in word)
{
if (cur.Children[c - 'a'] == null)
{
return false;
}
cur = cur.Children[c - 'a'];
}
return cur.IsWord;//注意此处
}
/** Returns if there is any word in the trie that starts with the given prefix. */
public bool StartsWith(string prefix)
{
var cur = root;
foreach (var c in prefix)
{
if (cur.Children[c - 'a'] == null)
{
return false;
}
cur = cur.Children[c - 'a'];
}
return true;
}
}
用字典实现
public class Trie
{
class TrieNode
{
public bool IsWord;
public Dictionary<char, TrieNode> ChildrenMap = new Dictionary<char, TrieNode>();
}
private TrieNode root = new TrieNode();
/** Initialize your data structure here. */
public Trie()
{ }
/** Inserts a word into the trie. */
public void Insert(string word)
{
TrieNode cur = root;
foreach (var c in word)
{
if (!cur.ChildrenMap.ContainsKey(c))
{
// insert a new node if the path does not exist
cur.ChildrenMap.Add(c, new TrieNode());
}
cur = cur.ChildrenMap[c];
}
cur.IsWord = true;
}
/** Returns if the word is in the trie. */
public bool Search(string word)
{
TrieNode cur = root;
foreach (var c in word)
{
if (!cur.ChildrenMap.ContainsKey(c))
{
return false;
}
cur = cur.ChildrenMap[c];
}
return cur.IsWord;//注意此处
}
/** Returns if there is any word in the trie that starts with the given prefix. */
public bool StartsWith(string prefix)
{
TrieNode cur = root;
foreach (var c in prefix)
{
if (!cur.ChildrenMap.ContainsKey(c))
{
return false;
}
cur = cur.ChildrenMap[c];
}
return true;
}
}
2. 前缀树的应用
累加所有以某前缀开头的串在字典中对应的值
leetcode题目地址
【思路】在TrieNode中加一个Value变量用来保存串对应的值,有Value的一定IsWord为true。找到前缀结尾,然后dfs遍历的过程中累加这个对应的值。
public class MapSum {
private MapSumNode root = new MapSumNode();
/** Initialize your data structure here. */
public MapSum() { }
public void Insert(string key, int val) {
var cur = root;
foreach(var c in key){
if(cur.Children[c-'a']==null){
cur.Children[c-'a'] = new MapSumNode();
}
cur = cur.Children[c-'a'];
}
cur.Value = val;
cur.IsWord = true;
}
public int Sum(string prefix) {
var cur = root;
foreach(var c in prefix){
if(cur.Children[c-'a']==null){
return 0;
}
cur = cur.Children[c-'a'];
}
return dfs(cur);
}
public int dfs(MapSumNode root){
var sum = root.Value;
foreach(var child in root.Children){
if(child!=null){
sum += dfs(child);
}
}
return sum;
}
}
public class MapSumNode
{
public int Value { get; set; }
public bool IsWord { get; set; }
public MapSumNode[] Children { get; set; }
public MapSumNode()
{
this.IsWord = false;
this.Children = new MapSumNode[26];
}
public MapSumNode(int value)
{
this.Value = value;
this.IsWord = false;
this.Children = new MapSumNode[26];
}
}
在Trie中搜索和DFS同步搜索
【题意】给定一组字符串,称为字典,给定一个字符矩阵,要求找出同时出现在字典中和字符矩阵中的字符串都有哪些,字符串出现在字符矩阵中的含义就是,从矩阵中某个字符开始,可以向上下左右四个方向走,走过的路径可以拼成这个字符串。
【思路】先把字典中的字符串都加入到Trie中,然后同步Trie中的搜索和二维矩阵(图)中的搜索,对于矩阵中每一个元素,都当作起点走一趟(dfs),dfs走到的节点同时也是Trie中走到的节点的儿子节点(Trie的root中啥都不放),然后Trie中走到的节点(当前节点)向下一步,dfs也把更新后的当前节点做为中转点沿着四个方向走下去。
public IList<string> FindWords(char[,] board, string[] words)
{
var res = new List<string>();
var root = Build(words);
for (var i = 0; i < board.GetLength(0); i++)
{
for (var j = 0; j < board.GetLength(1); j++)
{
dfs(board, i, j, root, res);
}
}
return res;
}
public void dfs(char[,] board, int i, int j, TrieNode p, List<string> res)
{
char c = board[i, j];
if (c == '0' || p.next[c - 'a'] == null) return;
p = p.next[c - 'a'];
if (!string.IsNullOrEmpty(p.word))
{
res.Add(p.word);
p.word = null;
}
board[i, j] = '0';
if (i > 0) dfs(board, i - 1, j, p, res);
if (j > 0) dfs(board, i, j - 1, p, res);
if (i < board.GetLength(0) - 1) dfs(board, i + 1, j, p, res);
if (j < board.GetLength(1) - 1) dfs(board, i, j + 1, p, res);
board[i, j] = c;
}
public TrieNode Build(string[] words)
{
var root = new TrieNode();
for (var i = 0; i < words.Length; i++)
{
TrieNode p = root;
for (var j = 0; j < words[i].Length; j++)
{
if (p.next[words[i][j] - 'a'] == null)
p.next[words[i][j] - 'a'] = new TrieNode();
p = p.next[words[i][j] - 'a'];
}
p.word = words[i];
}
return root;
}
public class TrieNode
{
public TrieNode[] next = new TrieNode[26];
public string word;
}