字典树——实现字符串前缀查找(可返回字符串)

字典树(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#迭代方法

posted @ 2021-11-02 16:27  zzjane摆大烂  阅读(264)  评论(0编辑  收藏  举报