【40讲系列11】字典树
一、理论
1. 基本结构
Trie树,即字典树或单词查找树。典型应用是用于统计和排序大量的字符串(不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。
优点是:最大限度地减少无谓的字符串比较,查询效率比哈希表高。
2. 核心思想
空间换时间。利用字符串的公共前缀来降低查询时间的开销以达到提高效率的目的。
3. 基本性质
从根节点到某一节点,路径上经过的字符连接起来,为该节点对应的字符串。
每个节点的所有子节点包含的字符都不相同。
二、典型例题
☆☆①:实现一个Trie字典树(LC208)【字节】
class Trie { // 内部类 private class Node { public boolean isWord; public Node[] children; // 记录孩子节点 public Node() { isWord = false; children = new Node[26]; } } private Node root; // 前缀树的根 /** Initialize your data structure here. */ public Trie() { root = new Node(); } /** Inserts a word into the trie. */ public void insert(String word) { Node cur = root; // 从根出发 for (char c : word.toCharArray()) { if (cur.children[c - 'a'] == null){ cur.children[c - 'a'] = new Node(); } cur = cur.children[c - 'a']; // 指向当前节点 } cur.isWord = true; //最后的节点为单词的最后一个单词,is_end设置为true } /** Returns if the word is in the trie. */ public boolean search(String word) { Node cur = root; // 从根出发 for (char c : word.toCharArray()) { 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 boolean startsWith(String prefix) { Node cur = root; // 从根出发 for (char c : prefix.toCharArray()) { if (cur.children[c - 'a'] == null) { return false; } cur = cur.children[c - 'a']; } return true; } }
☆☆☆☆②:二维网格中的单词搜索问题(LC212)
思路:DFS+字典树。 首先将每个单词都放入Trie中,然后在board[][]中搜索。
class Solution { List<String> res = new ArrayList<>(); public List<String> findWords(char[][] board, String[] words) { if (board.length == 0 || words.length == 0) return res; Trie trie = new Trie(); for (String word : words){ trie.insert(word); // 将word存入字典树 } boolean[][] isVisted = new boolean[board.length][board[0].length]; for (int i = 0; i < board.length; i++) { for (int j = 0; j < board[0].length; j++) { dfs(board,isVisted,i,j,trie.root); } } return res; } public void dfs(char[][] board, boolean[][] isVisted, int i, int j, Node cur){ if (i < 0 || i >= board.length || j < 0 || j >= board[0].length) return; if (isVisted[i][j]) return; char c =board[i][j]; cur = cur.children[c - 'a']; if (cur == null) return; if (cur.isWord){ res.add(cur.val); cur.isWord = false; // 去重操作,也可以使用HashSet } isVisted[i][j] = true; dfs(board,isVisted,i - 1,j,cur); dfs(board,isVisted,i + 1,j,cur); dfs(board,isVisted,i,j - 1,cur); dfs(board,isVisted,i,j + 1,cur); isVisted[i][j] = false; } } /** * 字典树 */ class Trie { public Node root; public Trie() { root = new Node(); } public void insert(String word){ Node cur = root; for (char c : word.toCharArray()){ if (cur.children[c - 'a'] == null){ cur.children[c - 'a'] = new Node(); } cur = cur.children[c - 'a']; } cur.isWord = true; cur.val = word; // 把单词存起来 } } public class Node { public String val; // 很关键,用来存单词 public boolean isWord; public Node[] children; public Node() { isWord = false; children = new Node[26]; // 单词由小写英文字母组成 } }