字典树PatriciaTrie的简单实现



public class PatriciaTrieMock<T> {
    //根节点
    private PatriciaTrieNode root;

    //设置一个节点类
    private class PatriciaTrieNode<T> {
        //该key是否是一个结束的标志位
        private boolean                   isWord;
        //key值
        private String                    key;
        //value值
        private T                         value;
        //子节点的数据
        private List<PatriciaTrieNode<T>> children = Lists.newArrayList();

        @Override
        public String toString() {
            return this.key + (this.isWord ? "#结束" : "") + "(子节点个数" + children.size() + ")";
        }
    }

    //插入数据(针对字典对象插入数据)
    public void insert(String key, T value) {
        //key值是否为空
        if (StringUtil.isEmpty(key)) {
            return;
        }
        //如果第一次进来,根节点为空
        if (root == null) {
            root = new PatriciaTrieNode();
        }
        insert(root, key, value);
    }

    /**
     * 删除
     *
     * @param key
     */
    private void delete(String key) {
        if (StringUtil.isEmpty(key) || root == null) {
            return;
        }
        delete(root, key);
    }

    private void delete(PatriciaTrieNode currentNode, String key) {
        for (int i = 0; i < currentNode.children.size(); i++) {
            PatriciaTrieNode chirdrenCurrentNode = (PatriciaTrieNode) currentNode.children.get(i);
            //取出最小长度,防止比较时出现越界问题
            int len = chirdrenCurrentNode.key.length() < key.length() ? chirdrenCurrentNode.key.length() : key.length();
            //匹配长度
            int matchLength = 0;
            for (; matchLength < len; matchLength++) {
                //不相等直接跳出
                if (key.charAt(matchLength) != chirdrenCurrentNode.key.charAt(matchLength)) {
                    break;
                }
            }
            //如果完全不匹配
            /**
             *
             *  root
             *  /   \
             * 最近#  杭州#
             * /  \       删除“杭州”
             * 三# 这些年#
             *
             */
            if (matchLength == 0) {
                //先比较删除的key首字母是不是应该排在当前节点key前面,如果是则说明没有找到
                if (key.charAt(matchLength) > chirdrenCurrentNode.key.charAt(matchLength)) {
                    //不满足则下一轮
                    continue;
                } else {
                    break;
                }

                //可以完全匹配上
                /**
                 * 1.要删除key的长度和当前节点key长度一致
                 * 2.要删除key的长度 > 当前节点key长度,需要递归删除
                 */
            } else if (matchLength == len) {

                if (key.length() == chirdrenCurrentNode.key.length()) {
                    /**
                     *  root
                     *  /
                     * 最近#
                     * /  \       删除“最近”
                     * 三# 这些年#
                     */
                    currentNode.children.remove(i);
                } else if (key.length() > chirdrenCurrentNode.key.length()) {
                    String childrenKey = key.substring(matchLength);
                    //递归调用delete方法插入
                    delete(chirdrenCurrentNode, childrenKey);
                    //如果子节点只有一个,且当前节点没有存放数据,需要merge
                    if (chirdrenCurrentNode.children.size() == 1 && !chirdrenCurrentNode.isWord) {
                        PatriciaTrieNode childrenNode = (PatriciaTrieNode) chirdrenCurrentNode.children.get(0);
                        childrenNode.key = chirdrenCurrentNode.key + childrenNode.key;
                        currentNode.children.set(i, childrenNode);
                    }
                }
            }
        }

    }

    //针对节点本身添加数据
    private void insert(PatriciaTrieNode currentNode, String key, T value) {
        Boolean isNeedInsert = true;
        //循环节点所有子节点插入数据
        for (int i = 0; i < currentNode.children.size(); i++) {
            PatriciaTrieNode chirdrenCurrentNode = (PatriciaTrieNode) currentNode.children.get(i);
            //取出最小长度,防止比较时出现越界问题
            int len = chirdrenCurrentNode.key.length() < key.length() ? chirdrenCurrentNode.key.length() : key.length();
            //匹配长度
            int matchLength = 0;
            for (; matchLength < len; matchLength++) {
                //不相等直接跳出
                if (key.charAt(matchLength) != chirdrenCurrentNode.key.charAt(matchLength)) {
                    break;
                }
            }
            //如果完全不匹配
            /**
             *
             *  root                      root
             *  /                         /   \
             * 最近#                    最近#    杭州#
             * /  \       插入“杭州”      /  \
             * 三# 这些年#              三# 这些年#
             *
             */
            if (matchLength == 0) {
                //先比较插入的key首字母是不是应该排在当前节点key前面
                //  杭<最
                if (key.charAt(matchLength) < chirdrenCurrentNode.key.charAt(matchLength)) {
                    PatriciaTrieNode patriciaTrieNodeNew = new PatriciaTrieNode();
                    currentNode.children.add(patriciaTrieNodeNew);
                    patriciaTrieNodeNew.key = key;
                    patriciaTrieNodeNew.value = value;
                    patriciaTrieNodeNew.isWord = true;
                    isNeedInsert = false;
                    break;
                } else {
                    //不满足则下一轮
                    continue;
                }

                //可以匹配上
            } else {
                //如果完全匹配,会有三种情况
                /**
                 * 插入的key长度小于当前节点key长度
                 * 插入的key长度大于当前节点key长度
                 * 插入的key长度等于当前节点key长度
                 */
                if (matchLength == len) {
                    /**
                     *1. 插入的key长度小于当前节点key长度
                     *  root                               root
                     *  /                                   /
                     * 最近#                              最#
                     * /  \       插入“最”                 /
                     * 三# 这些年#                       近#
                     *                                /  \
                     *                               三# 这些年#
                     *
                     */
                    if (key.length() < chirdrenCurrentNode.key.length()) {
                        //设置子节点数据
                        String childrenKey = chirdrenCurrentNode.key.substring(matchLength);
                        PatriciaTrieNode patriciaTrieNode = new PatriciaTrieNode();
                        patriciaTrieNode.key = childrenKey;
                        patriciaTrieNode.isWord = chirdrenCurrentNode.isWord;
                        patriciaTrieNode.value = chirdrenCurrentNode.value;
                        //设置当前节点的数据
                        chirdrenCurrentNode.key = key;
                        chirdrenCurrentNode.value = value;
                        chirdrenCurrentNode.isWord = true;
                        chirdrenCurrentNode.children = Lists.newArrayList();
                        chirdrenCurrentNode.children.add(patriciaTrieNode);

                        /**
                         *2. 插入的key长度大于当前节点key长度
                         *  root                               root
                         *  /                                   /
                         * 最近#                                最近#
                         * /  \       插入“最近好难”           /   \   \
                         * 三# 这些年#                      三# 这些年#  好难#
                         */
                    } else if (key.length() > chirdrenCurrentNode.key.length()) {
                        String childrenKey = key.substring(matchLength);
                        //递归调用insert方法插入
                        insert(chirdrenCurrentNode, childrenKey, value);

                        /**
                         *3. 插入的key长度等于当前节点key长度
                         *  root                   root
                         *  /                      /
                         * 最近#                    最近#
                         * /  \       插入“最近”      /  \
                         * 三# 这些年#                三# 这些年#
                         *
                         */
                    } else {
                        //新值覆盖旧值
                        chirdrenCurrentNode.value = value;
                        chirdrenCurrentNode.isWord = true;
                    }

                    //只能匹配一部分
                    /**
                     *3. 插入的key长度等于当前节点key长度
                     *  root                   root
                     *  /                       /
                     * 最近#                      最#
                     * /  \       插入“最少金额”  /   \
                     * 三# 这些年#               近#   少金额#
                     *                         /  \
                     *                       三# 这些年#
                     */
                } else {
                    //设置插入key的子节点
                    String newKey = key.substring(matchLength);
                    PatriciaTrieNode patriciaTrieNodeNew = new PatriciaTrieNode();
                    patriciaTrieNodeNew.key = newKey;
                    patriciaTrieNodeNew.isWord = true;
                    patriciaTrieNodeNew.value = value;

                    //设置当前节点的子节点
                    String childrenKey = chirdrenCurrentNode.key.substring(matchLength);
                    PatriciaTrieNode patriciaTrieNodeChildren = new PatriciaTrieNode();
                    patriciaTrieNodeChildren.key = childrenKey;
                    patriciaTrieNodeChildren.isWord = chirdrenCurrentNode.isWord;
                    patriciaTrieNodeChildren.value = chirdrenCurrentNode.value;
                    patriciaTrieNodeChildren.children = chirdrenCurrentNode.children;

                    //设置当前节点的最新值
                    chirdrenCurrentNode.key = chirdrenCurrentNode.key.substring(0, matchLength);
                    chirdrenCurrentNode.isWord = false;
                    chirdrenCurrentNode.value = null;
                    chirdrenCurrentNode.children = Lists.newArrayList();
                    //比较添加
                    if (newKey.charAt(0) < childrenKey.charAt(0)) {
                        chirdrenCurrentNode.children.add(patriciaTrieNodeNew);
                        chirdrenCurrentNode.children.add(patriciaTrieNodeChildren);
                    } else {
                        chirdrenCurrentNode.children.add(patriciaTrieNodeChildren);
                        chirdrenCurrentNode.children.add(patriciaTrieNodeNew);
                    }

                }
                //只要能匹配上一个字符,后续的循环都跳出
                isNeedInsert = false;
                break;
            }

        }
        //如果没有子节点,则直接把key放在在一个新生成的子节点上
        /**
         * 1.key的首字符需要放在所有子节点后面
         * 2.当前节点没有子节点
         *  root                            root
         *  /                               /
         * 最近#                          最近#
         * /  \       插入“最近三年”      /  \
         * 三# 这些年#                三# 这些年#
         *                            /
         *                           年#
         */
        if (isNeedInsert) {
            PatriciaTrieNode patriciaTrieNode = new PatriciaTrieNode();
            patriciaTrieNode.key = key;
            patriciaTrieNode.value = value;
            patriciaTrieNode.isWord = true;
            currentNode.children.add(patriciaTrieNode);
        }
    }

    /**
     * 前缀匹配
     *
     * @param key
     */
    private List<T> prefixMatch(String key) {
        if (StringUtil.isEmpty(key) || root == null) {
            return null;
        }
        return prefixMatch(root, key);
    }

    /**
     * 前缀匹配
     *
     * @param root
     * @param key
     * @return
     */
    private List<T> prefixMatch(PatriciaTrieNode root, String key) {
        List<T> result = Lists.newArrayList();
        for (int i = 0; i < root.children.size(); i++) {
            PatriciaTrieNode chirdrenCurrentNode = (PatriciaTrieNode) root.children.get(i);
            //取出最小长度,防止比较时出现越界问题
            int len = chirdrenCurrentNode.key.length() < key.length() ? chirdrenCurrentNode.key.length() : key.length();
            //匹配长度
            int matchLength = 0;
            for (; matchLength < len; matchLength++) {
                //不相等直接跳出
                if (key.charAt(matchLength) != chirdrenCurrentNode.key.charAt(matchLength)) {
                    break;
                }
            }
            //一个都没匹配上
            /**
             * 1. 查询key的首字母比当前节点key首字母大,那么有可能在后续节点匹配
             * 2. 查询key的首字母比当前节点key首字母小,那么确实匹配不上
             */

            /**
             *  查询的key为“好难呀” or “真的”
             *  root
             *  /   \
             * 最近#  好难呀#
             * /  \
             * 三# 这些年#
             *
             *
             */
            if (matchLength == 0) {
                if (key.charAt(matchLength) < chirdrenCurrentNode.key.charAt(matchLength)) {
                    break;
                } else {
                    continue;
                }
                //如果完全匹配(匹配长度==最小长度)
            } else if (matchLength == len) {
                /**
                 *  查询的key为“最近这”
                 *  root
                 *  /   \
                 * 最近#  好难呀#
                 * /  \
                 * 三# 这些年#
                 */
                if (key.length() > chirdrenCurrentNode.key.length()) {
                    String findKey = key.substring(matchLength);
                    result = prefixMatch(chirdrenCurrentNode, findKey);
                } else {
                    /**
                     *  查询的key为“最近” 或者"最"
                     *  root
                     *  /   \
                     * 最近#  好难呀#
                     * /  \
                     * 三# 这些年#
                     */
                    if (chirdrenCurrentNode.isWord) {
                        result.add((T) chirdrenCurrentNode.value);
                    }
                    //获取子节点的值
                    getChildrenValue(chirdrenCurrentNode, result);
                }
            }
            //如果不能完全匹配,则相当于匹配不上任何值
            /**
             *  查询的key为“好难搞”
             *  root
             *  /   \
             * 最近#  好难呀#
             * /  \
             * 三# 这些年#
             */
        }
        return result;
    }

    /**
     * 递归获取子节点的值
     *
     * @param chirdrenCurrentNode
     * @param result
     */
    private void getChildrenValue(PatriciaTrieNode<T> chirdrenCurrentNode,
                                  List<T> result) {
        for (int i = 0; i < chirdrenCurrentNode.children.size(); i++) {
            PatriciaTrieNode patriciaTrieNode = chirdrenCurrentNode.children.get(i);
            if (CollectionUtils.isNotEmpty(patriciaTrieNode.children)) {
                getChildrenValue(patriciaTrieNode, result);
            }
            if (patriciaTrieNode.isWord) {
                result.add((T) patriciaTrieNode.value);
            }
        }
    }

    public static void main(String[] args) {
        PatriciaTrieMock<String> patriciaTrieMock = new PatriciaTrieMock<String>();
        patriciaTrieMock.insert("最近三年", "最近三年");
        patriciaTrieMock.insert("最近有点难", "最近有点难");
        patriciaTrieMock.insert("你好", "你好");
        patriciaTrieMock.insert("这是patriciaTrieste的实现", "这是patriciaTrieste的实现");
        patriciaTrieMock.insert("这是xxx的实现", "这是xxx的实现");
        System.out.println(patriciaTrieMock);
        List<String> result = patriciaTrieMock.prefixMatch("最近");
        result.stream().forEach(obj -> System.out.println(obj));
        patriciaTrieMock.delete("这是xxx的实现");
        result = patriciaTrieMock.prefixMatch("这是");
        result.stream().forEach(obj -> System.out.println(obj));

    }

}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Trie字典的Java代码实现可以分为以下几部分: 1. 定义Trie节点类,包含children数组和isEndOfWord标识,用于表示是否是单词的结尾。 2. 定义Trie类,包含插入、查找和删除操作。 3. 在Trie类中实现插入操作,遍历字符串每一个字符,在Trie中寻找对应节点,如果不存在则新建节点。 4. 在Trie类中实现查找操作,遍历字符串每一个字符,在Trie中寻找对应节点,如果找到最后一个字符对应的节点的isEndOfWord标识为true,则说明字符串是单词。 5. 在Trie类中实现删除操作,遍历字符串每一个字符,在Trie中寻找对应节点,如果找到最后一个字符对应的节点的isEndOfWord标识为true,则将其设为false,并删除空节点。 如果需要完整代码和注释请告诉我。 ### 回答2: Trie(字典)是一种常用的数据结构,用于高效地存储和查找字符串。下面是Trie字典的Java代码实现,用于返回单词。 ```java class TrieNode { private TrieNode[] children; private boolean isEndOfWord; public TrieNode() { children = new TrieNode[26]; // 字母表的大小为26 isEndOfWord = false; } public void insert(String word) { TrieNode curr = this; for (char c : word.toCharArray()) { if (curr.children[c - 'a'] == null) { curr.children[c - 'a'] = new TrieNode(); } curr = curr.children[c - 'a']; } curr.isEndOfWord = true; } public boolean search(String word) { TrieNode node = searchPrefix(word); return node != null && node.isEndOfWord; } public boolean startsWith(String prefix) { TrieNode node = searchPrefix(prefix); return node != null; } private TrieNode searchPrefix(String prefix) { TrieNode curr = this; for (char c : prefix.toCharArray()) { if (curr.children[c - 'a'] == null) { return null; } curr = curr.children[c - 'a']; } return curr; } } public class Trie { private TrieNode root; public Trie() { root = new TrieNode(); } public void insert(String word) { root.insert(word); } public boolean search(String word) { return root.search(word); } public boolean startsWith(String prefix) { return root.startsWith(prefix); } } public class Main { public static void main(String[] args) { Trie trie = new Trie(); trie.insert("apple"); trie.insert("app"); System.out.println(trie.search("apple")); // 输出:true System.out.println(trie.startsWith("app")); // 输出:true System.out.println(trie.search("banana")); // 输出:false } } ``` 以上代码中,`TrieNode`表示Trie的节点,`Trie`表示Trie的结构。其中`TrieNode`类包含了插入单词、查找单词(完全匹配)以及查找前缀的功能。`Trie`类则是对外提供插入、查找单词和前缀的方法。 在`main`方法中,我们演示了如何使用`Trie`类来插入和查找单词。首先,我们插入了两个单词"apple"和"app"。然后分别调用`search`方法来查找"apple"和"banana",以及`startsWith`方法来查找以"app"开头的单词。最后,打印出对应的结果,即是否找到了对应的单词或前缀。 以上是Trie字典的Java代码实现,用于返回单词。 ### 回答3: Trie字典是一种经典的数据结构,用于高效地存储和查找字符串集合。下面是一个基于Java的Trie字典的代码实现,可以实现返回单词的功能: ```java class TrieNode { private final int ALPHABET_SIZE = 26; private TrieNode[] children; private boolean isEndOfWord; public TrieNode() { children = new TrieNode[ALPHABET_SIZE]; isEndOfWord = false; } } class Trie { private TrieNode root; public Trie() { root = new TrieNode(); } public void insert(String word) { TrieNode current = root; for (int i = 0; i < word.length(); i++) { char ch = word.charAt(i); int index = ch - 'a'; if (current.children[index] == null) { current.children[index] = new TrieNode(); } current = current.children[index]; } current.isEndOfWord = true; } public boolean search(String word) { TrieNode current = root; for (int i = 0; i < word.length(); i++) { char ch = word.charAt(i); int index = ch - 'a'; if (current.children[index] == null) { return false; } current = current.children[index]; } return current != null && current.isEndOfWord; } public List<String> getAllWords() { List<String> result = new ArrayList<>(); TrieNode current = root; StringBuilder sb = new StringBuilder(); getAllWordsUtil(current, sb, result); return result; } private void getAllWordsUtil(TrieNode node, StringBuilder sb, List<String> result) { if (node == null) { return; } if (node.isEndOfWord) { result.add(sb.toString()); } for (int i = 0; i < ALPHABET_SIZE; i++) { if (node.children[i] != null) { sb.append((char)('a' + i)); getAllWordsUtil(node.children[i], sb, result); sb.deleteCharAt(sb.length() - 1); } } } } public class Main { public static void main(String[] args) { Trie trie = new Trie(); String[] words = {"hello", "world", "java", "programming"}; for (String word : words) { trie.insert(word); } List<String> allWords = trie.getAllWords(); System.out.println("All words in trie: " + allWords); } } ``` 上述代码中,TrieNode类表示字典的节点,包括一个指向子节点的数组和一个标记,用于表示节点是否是某个单词的结尾。Trie类封装了字典的操作,包括插入单词、查找单词和返回所有单词的功能。在代码的主函数中,我们创建一个Trie对象并插入一些单词,然后调用getAllWords()方法返回字典中的所有单词。最后,打印出返回的单词列表。 希望以上解答对您有所帮助,如有更多疑问,请继续追问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值