zoukankan      html  css  js  c++  java
  • 字典树——实现字符串前缀查找(可返回字符串)

    字典树(Trie树)——实现字符串前缀查找

    写在前边:
    那什么阿里云八十几行代码的比赛的第二题,类似于使用Idea写Java代码时候出现的API搜索提示,看网上还有google海量搜索之类的应用,这部分就暂且不表。
    与本题的思路就是定义节点,声明树,插入节点新建树,之后进行前缀搜索、全路径查找等。
    为了降低时间复杂度,使用非递归方法进行查找,转换思路就是多叉树的深度优先遍历,用了两个栈。
    感觉path是没有必要的,但是也没其他想法实现本字符串的查找。
    HashMap也可以换成数组。String也可以换成char。
    但是全路径查找过于复杂,所以当时只顾得上借鉴实现,也没有改。

     
     

    1.首先定义节点类

    public class TreeNode {
    
        //当前节点字符
        public String content;
        //存储子节点
        public Map<String,TreeNode> subNodes ;
        //存储有多少个前缀
        public int path;
    
        public TreeNode(){
            subNodes =  new HashMap<>();
            path = 0;
        }
        //获取当前字符
        public String getContent() {
            return content;
        }
        //设置当前字符
        public void setContent(String content) {
            this.content = content;
        }
        @Override
        public String toString() {
            return "TreeNode[" +
                    "content=" + content +
                    ", subNodes=" + Arrays.toString(subNodes.keySet().toArray()) +
                    ']';
        }
    }
    

    2.实现树的建立

    public class TrieTree {
    
        public TreeNode root;
        public TrieTree() {
            root = new TreeNode();
        }
        //插入字符串
        public void insertString(String str){
            if(str ==null||str.length()==0){
                return;
            }
            int length=str.length();
            TreeNode nowNode= root;
            nowNode.path++;
            for(int i=0;i<length;i++){
                char now = str.charAt(i);
                //index为字符now所处的位置
                if(!nowNode.subNodes.containsKey(String.valueOf(now))){
                    TreeNode newnode = new TreeNode();
                    newnode.setContent(String.valueOf(now));
                    nowNode.subNodes.put(String.valueOf(now),newnode);
                }
    
                nowNode = nowNode.subNodes.get(String.valueOf(now));
                nowNode.path++;
            }
        }
    

    3.实现节点返回,数目查找

        //返回前缀节点(该结构下返回该字符串的最后一个字符所在节点)
        public TreeNode searchPrefix(String str) {
            TreeNode nowNode= root;
            char[] chars = str.toCharArray();
            for (char ch : chars) {
                if (!nowNode.subNodes.containsKey(String.valueOf(ch)))
                    return null;
                nowNode = nowNode.subNodes.get(String.valueOf(ch));
            }
            return nowNode;
        }
    
        //返回包含该前缀的字符串数目
        public int preNum(String str){
            TreeNode nowNode = root;
            int length=str.length();
            for(int i=0;i<length;i++){
                char now = str.charAt(i);
                //index为字符now所处的位置
                if(!nowNode.subNodes.containsKey(String.valueOf(now)))
                  return 0;
    
                nowNode = nowNode.subNodes.get(String.valueOf(now));
            }
            return nowNode.path;
        }
    

    4.前缀字符串的查找,

    非递归方法,相当于多叉树的全路径查找,参考了另外一个博客的实现

    //获取完整字符串
        public List<String> findAllPost(TreeNode nowNode) {
            Deque<TreeNode> majorstack = new ArrayDeque<>();
            //副栈
            Deque<TreeNode> minorstack = new ArrayDeque<>();
            // 存一个节点的子节点个数
            HashMap<TreeNode, Integer> sonCountNode = new HashMap<>();
            minorstack.addLast(nowNode);
            //保存结果
            List<String> res = new ArrayList<>();
            String curString = "";
            while (!minorstack.isEmpty()) {
                //该要处理的节点,出副栈,进入主栈
                TreeNode unusedNode = minorstack.pollLast();
                majorstack.addLast(unusedNode);
                curString += unusedNode.getContent();
                if (!unusedNode.subNodes.isEmpty()) {
                    //若不是叶子节点,将该要处理的节点S的子节点全部纳入副栈
                    Map<String, TreeNode> a = unusedNode.subNodes;
                    Iterator<Map.Entry<String, TreeNode>> iterator = a.entrySet().iterator();
                    while (iterator.hasNext()) {
                        Map.Entry entry = (Map.Entry) iterator.next();
                        TreeNode child = (TreeNode) entry.getValue();
                        minorstack.addLast(child);
                    }
                }
    
                TreeNode majNode = majorstack.peekLast();
                //栈顶为叶子节点 或 栈顶节点孩子节点遍历完了
                while (majNode.subNodes.size() == 0 || (sonCountNode.get(majNode) != null && sonCountNode.get(majNode).equals(majNode.subNodes.size()))) {
                    TreeNode last = majorstack.pollLast();
                    majNode = majorstack.peekLast();
                    if (majNode == null) {
                       return res;
                    }
                    if (sonCountNode.get(majNode) == null) {
                        sonCountNode.put(majNode, 1);
                    } else {
                        sonCountNode.put(majNode, sonCountNode.get(majNode) + 1);
                    }
                    if (last.subNodes.isEmpty()) {
                        res.add(curString);
                    }
                    curString = curString.substring(0, curString.length() - 1);
                }
            }
            return res;
        }
    

    Test代码

    public class Main {
    
        public static void main(String[] args) {
    
            TrieTree tree = new TrieTree();
            tree.insertString("SqlTrimFunction");
            tree.insertString("SqlBinaryOperator");
            tree.insertString("SqlSessionManager");
            tree.insertString("SqlInQueryCallback");
            tree.insertString("SqlIserDefinedFunction");
    
            Map<String, List<String>> result = new HashMap<>();
            List<String> preList = new ArrayList<>();
            preList.add("Sql");
           for(int j = 0;j<preList.size();j++ ){
                String pre = preList.get(j);
                TreeNode preLast = tree.searchPrefix(pre);
                int count = tree.preNum(pre);
                List<String> res2 = new ArrayList<>();
                if (count == 0 || pre.length() == 0) {
                    result.put(pre, res2);
                    } else {
                        List<String> res1 = tree.findAllPost(preLast);
                        for (int i = 0; i < res1.size(); i++) {
                            String completedword = pre.substring(0, pre.length() - 1) + res1.get(i);
                            res2.add(completedword);
                        }
                    }
                    if (count - res2.size() == 1) {
                        res2.add(pre);
                    }
                    result.put(pre, res2);
                }
            System.out.println(result);
        }
    }
    
    

    结果

    {Sql=[SqlInQueryCallback, SqlIserDefinedFunction, SqlTrimFunction, SqlSessionManager, SqlBinaryOperator]}
    
    

    参考:

    https://www.jianshu.com/p/e431bd41d676
    https://www.cnblogs.com/hwtblog/p/10940692.html#迭代方法

  • 相关阅读:
    临时禁用大型列表上的列表视图阈值
    临时禁用大型列表上的列表视图阈值
    SharePoint Foundation 2010 托管客户端对象模型概述 创建 Windows 控制台托管的客户端对象模型应用程序
    如何隐藏MOSS2010的网站操作菜单
    MOSS2010的列表视图参数设置以及列表记录样式设置
    使用SharePoint Server 2010搜索PDF文档
    Sharepoint2010如何使用Linq to Sharepoint
    水晶報表字段超鏈結4/28
    4月21自考4/23
    [轉]統籌方法華羅庚
  • 原文地址:https://www.cnblogs.com/alwayszzj/p/15499634.html
Copyright © 2011-2022 走看看