本文主要是介绍力扣labuladong——一刷day90,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
- 前言
- 一、Trie树实现
前言
Trie 树又叫字典树、前缀树、单词查找树,是一种二叉树衍生出来的高级数据结构,主要应用场景是处理字符串前缀相关的操作
一、Trie树实现
public class TrieMap<V> {//ASCII码个数private static final int R = 256;//当前存在map中的键值对的个数private int size = 0;//Trie 树的根节点private TrieNode<V> root = null;//孩子节点(私有静态类)private static class TrieNode<V>{V val = null;TrieNode<V>[] children = new TrieNode[R];}/*** 从节点node开始搜索key如果存在则返回,不存在返回null(寻找的是节点)* @param node* @param key* @return*/public TrieNode<V> getNode(TrieNode<V>node, String key){TrieNode<V> p = node;for (int i = 0; i < key.length(); i++) {char c = key.charAt(i);if (p == null){return null;}p = p.children[c];}return p;}/*** 获取key值对应的value* @param key* @return*/public V get(String key){TrieNode<V> node = getNode(root,key);if(node != null && node.val != null){return node.val;}return null;}/*** 判断是否存在key* @param key* @return*/public boolean containsKey(String key){return get(key) != null;}/*** 是否存在前缀为predix的键* @param prefix* @return*/public boolean hasKeyWithPrefix(String prefix){return getNode(root,prefix) != null;}/*** 在所有键中寻找query的最短前缀* @param query* @return*/public String shortestPrefixOf(String query){TrieNode<V> p = root;for (int i = 0; i < query.length(); i++) {char c = query.charAt(i);if(p == null){return "";}if(p.val != null){return query.substring(0,i);}p = p.children[c];}if (p != null && p.val != null){return query;}return "";}/*** 在所有键中寻找query最长前缀* @param query* @return*/public String longestPrefixOf(String query){int maxLen = 0;TrieNode<V> p = root;for (int i = 0; i < query.length(); i++) {if(p == null){return "";}char c = query.charAt(i);if(p.val != null){maxLen = i;}p = p.children[c];}if (p != null && p.val != null){return query;}return query.substring(0,maxLen);}/*** 找到所以以prefix为前缀的字符串* @param prefix* @return*/public List<String> keysWithPrefix(String prefix){List<String> res = new LinkedList<>();TrieNode<V> node = getNode(root,prefix);if (node == null){return res;}traverse(node, new StringBuilder(prefix),res);return res;}/*** 遍历以node节点为根的Trie树,找到所有的键* @param node* @param sb* @param res*/private void traverse(TrieNode<V> node, StringBuilder sb, List<String> res) {if (node == null){return;}if (node.val != null){res.add(sb.toString());}for (char i = 0; i < R; i++) {sb.append(i);traverse(node.children[i],sb,res);sb.deleteCharAt(sb.length() -1);}}/*** 匹配模式串pattern得到所有的key* @param pattern* @return*/public List<String> keysWithPattern(String pattern){List<String> res = new LinkedList<>();traverse(root, new StringBuilder(), pattern, 0, res);return res;}/*** 遍历函数,尝试在「以 node 为根的 Trie 树中」匹配 pattern[i..]* @param node* @param path* @param pattern* @param i* @param res*/private void traverse(TrieNode<V> node, StringBuilder path, String pattern, int i, List<String> res) {if (node == null){return;}if (i == path.length()){if (node.val != null){res.add(path.toString());}return;}char ch = pattern.charAt(i);if (ch == '.'){for (char c = 0; c < R; c ++){path.append(c);traverse(node.children[c],path,pattern,i+1,res);path.deleteCharAt(path.length()-1);}}else {path.append(ch);traverse(node.children[ch],path,pattern,i +1,res);path.deleteCharAt(path.length()-1);}}/*** 判断是否存在模式串pattern* @param pattern* @return*/public boolean hasKeyWithPattern(String pattern){return hasKeyWithPattern(root,pattern, 0);}/*** 函数定义:从 node 节点开始匹配 pattern[i..],返回是否成功匹配* @param node* @param pattern* @param i* @return*/private boolean hasKeyWithPattern(TrieNode<V> node, String pattern, int i) {if (node == null){return false;}if (i == pattern.length()){if (node.val != null){return true;}}char ch = pattern.charAt(i);if (ch == '.'){for (char c = 0; c < R; c ++){if (hasKeyWithPattern(node.children[ch],pattern, i +1)){return true;}}}else {return hasKeyWithPattern(node.children[ch],pattern,i+1);}return false;}/*** 在TrieMap中添加或者修改键值对* @param key* @param val*/public void put(String key, V val){if (!containsKey(key)){size ++;}root = put(root, key, val,0);}/*** 定义:向以 node 为根的 Trie 树中插入 key[i..],返回插入完成后的根节点* @param node* @param key* @param val* @param i* @return*/private TrieNode<V> put(TrieNode<V> node, String key, V val, int i) {if (node == null){node = new TrieNode<>();}if (i == key.length()){node.val = val;return node;}char c = key.charAt(i);node.children[c] = put(node.children[c],key,val,i+1);return node;}/*** 在 Map 中删除 key* @param key*/public void remove(String key) {if (!containsKey(key)) {return;}// 递归修改数据结构要接收函数的返回值root = remove(root, key, 0);size--;}/*** 定义:在以 node 为根的 Trie 树中删除 key[i..],返回删除后的根节点* @param node* @param key* @param i* @return*/private TrieNode<V> remove(TrieNode<V> node, String key, int i) {if (node == null) {return null;}if (i == key.length()) {// 找到了 key 对应的 TrieNode,删除 valnode.val = null;} else {char c = key.charAt(i);// 递归去子树进行删除node.children[c] = remove(node.children[c], key, i + 1);}// 后序位置,递归路径上的节点可能需要被清理if (node.val != null) {// 如果该 TireNode 存储着 val,不需要被清理return node;}// 检查该 TrieNode 是否还有后缀for (int c = 0; c < R; c++) {if (node.children[c] != null) {// 只要存在一个子节点(后缀树枝),就不需要被清理return node;}}// 既没有存储 val,也没有后缀树枝,则该节点需要被清理return null;}
}
这篇关于力扣labuladong——一刷day90的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!