饭后小甜点leetcode——前缀树及其应用

文章目录

      • 前缀树
        • 1. 前缀树的实现
          • 用数组实现
          • 用字典实现
        • 2. 前缀树的应用
          • 累加所有以某前缀开头的串在字典中对应的值
          • 在Trie中搜索和DFS同步搜索

前缀树

前缀树的几种操作

  • 建树/插入
  • 查找单词是否在树中
  • 判断是否以某一前缀开头(StartsWith)

TrieNode需要的成员

  • bool IsWord,记录从root到当前节点的路径是否组成单词
  • TrieNode[] Children / Dictionary,孩子节点们
  • 该node对应的一些值什么的(可选,看题目)

1. 前缀树的实现

leetcode题目地址

用数组实现
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同步搜索

leetcode题目地址

【题意】给定一组字符串,称为字典,给定一个字符矩阵,要求找出同时出现在字典中和字符矩阵中的字符串都有哪些,字符串出现在字符矩阵中的含义就是,从矩阵中某个字符开始,可以向上下左右四个方向走,走过的路径可以拼成这个字符串。

【思路】先把字典中的字符串都加入到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;
}

你可能感兴趣的:(基础算法)