字典树——实现字符串前缀查找(可返回字符串)
字典树(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#迭代方法