前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >字典树 Krains 2020-09-01

字典树 Krains 2020-09-01

作者头像
Krains
发布2020-09-10 18:38:59
3880
发布2020-09-10 18:38:59
举报
文章被收录于专栏:Krains

应用

  1. 搜索引擎的自动补全
  2. 拼写检查

当然还有其他的数据结构,如哈希表,使我们能够在字符串数据集中搜索单词。为什么我们还需要 Trie 树呢?尽管哈希表可以在 O(1) 时间内寻找键值,却无法高效的完成以下操作: 找到具有同一前缀的全部键值。

Trie 树优于哈希表的另一个理由是,随着哈希表大小增加,会出现大量的冲突,时间复杂度可能增加到 O(n),其中 n 是插入的键的数量。与哈希表相比,Trie 树在存储多个具有相同前缀的键时可以使用较少的空间, 查找键值Trie 树只需要 O(m) 的时间复杂度,其中 m 为键长。

定义字典树数据结构

代码语言:javascript
复制
	// 字典树数据结构,isEnd标记当前结点是否为一个单词的末尾,即表示该路径下是不是一个完整的单词
    // 用map存储下一个字符和其对应的结点,字典树的根不表示任何字符
 	class TireNode {
        private boolean isEnd = false;
        private Map<Character, TireNode> subNode = new HashMap<>();

        public boolean isEnd() {
            return isEnd;
        }

        public void setEnd() {
            isEnd = true;
        }

        public TireNode getChild(Character c) {
            return subNode.get(c);
        }

        public void addChild(Character c, TireNode node) {
            subNode.put(c, node);
        }
    }

一些常用的字典树有关方法

代码语言:javascript
复制
class Trie{
    private TireNode root = new TireNode();

    // 没有处理word为空串或者为空字符""的情况
    // Insert a word into the tire.
    public void insert(String word) {
        TireNode temp = root;
        for (int i = 0; i < word.length(); i++) {
            char c = word.charAt(i);
            if (temp.getChild(c) == null) {
                temp.addChild(c, new TireNode());
            }
            temp = temp.getChild(c);
            // 标记叶子结点
            if (i == word.length() - 1)
                temp.setEnd();
        }
    }
    
    /** Returns if the word is in the trie. */
    public boolean search(String word) {
        TireNode temp = root;
        for(int i = 0; i < word.length(); i++){
            char c = word.charAt(i);
			temp = temp.getChild(c);
            if(temp == null){
            	return false;
            }
        }
        return temp.isEnd();
    }
    
    /** Returns if there is any word in the trie that starts with the given prefix. */
    public boolean startsWith(String prefix) {
        TireNode temp = root;
        for(int i = 0; i < prefix.length(); i++){
            char c = prefix.charAt(i);
			temp = temp.getChild(c);
            if(temp == null){
            	return false;
            }
        }
        return true;
    }
}

经典例题

212. 单词搜索 II

给定一个二维网格 board 和一个字典中的单词列表 words,找出所有同时在二维网格和字典中出现的单词。

单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母在一个单词中不允许被重复使用。

示例:

代码语言:javascript
复制
输入: 
words = ["oath","pea","eat","rain"] and board =
[
  ['o','a','a','n'],
  ['e','t','a','e'],
  ['i','h','k','r'],
  ['i','f','l','v']
]

输出: ["eat","oath"]

涉及到匹配多个单词,用字典树。

将words存入字典树,采用回溯算法遍历字典树匹配所有可能出现的单词。

代码语言:javascript
复制
class Solution {
        TireNode root = new TireNode();

    public void insert(String word) {
        TireNode temp = root;
        for (int i = 0; i < word.length(); i++) {
            char c = word.charAt(i);
            if (temp.getChild(c) == null) {
                temp.addChild(c, new TireNode());
            }
            temp = temp.getChild(c);
            // 标记叶子结点
            if (i == word.length() - 1)
                temp.setEnd();
        }
    }

    List<String> ans = new ArrayList<>();
    Set<String> set = new HashSet<>();
    int[][] d = new int[][]{{1,0},{-1,0},{0,1},{0,-1}};
    int n;
    int m;

    public List<String> findWords(char[][] board, String[] words) {
        for(String word : words){
            insert(word);
        }
        m = board.length;
        n = m == 0 ? 0 : board[0].length;
        for(int i = 0; i < m; i++){
            for(int j = 0; j < n; j++){
                dfs(board, root.getChild(board[i][j]), i, j, new StringBuilder(), new boolean[m][n]);
            }
        }
        for(String word : set){
            ans.add(word);
        }
        return ans;
    }

    // 回溯查找单词 
    public void dfs(char[][] board, TireNode temp, int x, int y,StringBuilder sb, boolean[][] visited){
        // 该单词不在字典树,返回
        if(temp == null)
            return ;
        sb.append(board[x][y]);
        visited[x][y] = true;
        // 完整的一个单词在字典树,加入集合,并继续向下搜索
        if(temp.isEnd()){
            set.add(sb.toString());
        }
        for(int i = 0; i < 4; i++){
            int tx = x + d[i][0];
            int ty = y + d[i][1];
            if(tx >= 0 && tx < m && ty >= 0 && ty < n && visited[tx][ty] == false){
                dfs(board, temp.getChild(board[tx][ty]), tx, ty, sb, visited);
            }
        }
        // 回溯
        sb.delete(sb.length()-1, sb.length());
        visited[x][y] = false;
    }
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020-09-01,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 定义字典树数据结构
  • 经典例题
    • 212. 单词搜索 II
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档