输入法只需要实现三个非常重要的函数即可:
- /**
- * 插入一个单词
- *
- * @param term
- * 要插入的单词
- */
- public void insert(String term);
- /**
- * 删除一个单词
- *
- * @param term
- * 要删除的单词
- */
- public void delete(String term);
- /**
- * 获取提示的词条
- *
- * @param preTerm
- * 用户已输入的词条
- * @return 提示词条
- */
- public List<String> promptsTerms(String preTerm);
当然在考虑删除某个单词的时候,由于有很多单词共享相同的前缀,所以不能轻易删除,具体删除的情况可以分为三种:完全独立型、完全共享型、部分共享型。大家自己去思考吧,还是很简单的,我就不说了~
其中一种实现思路可以为每个单词节点设计一个计数器,delete的时候就把计数减1,当计数为0时就删除。但这种方法有个缺陷:即每个节点都要多设置一个计数的空间,节点当然不少,无形中对空间会造成比较大的浪费。而且对于输入法而言delete函数并不常用,为了一个极少使用的函数浪费这么多空间肯定是不合适的。特别是对于手机而言,本身内存就已经很少了,这样的设计肯定是bad smell。那么,我们采用另外一种方法去解决了这个问题~
废话我就不太多说了,我觉得用代码描述更为直观,大家有什么问题直接研究代码吧~
- package com.algorithm;
- import java.util.ArrayList;
- import java.util.HashMap;
- import java.util.List;
- import java.util.Map;
- import java.util.Stack;
- /**
- * 共享前缀树PrefixTree
- *
- * E-mail:530025983@qq.com 2011-5-28
- *
- * @author MONKEY_D_MENG
- *
- */
- public class PrefixTree
- {
- /* 利用Hash表能够O(1)时间内找到下一个字符的位置 */
- private Map<Byte, PrefixTree> wordTable = new HashMap<Byte, PrefixTree>();
- /* 到这个节点为止是否是一个完整的单词 */
- private boolean isWord = false;
- /**
- * 插入一个单词
- *
- * @param term
- * 要插入的单词
- */
- public void insert(String term)
- {
- if (null == term)
- {
- return;
- }
- /* 将单词转化为字符串处理 */
- byte[] termChars = term.getBytes();
- PrefixTree current = this;
- for (byte ch : termChars)
- {
- if (!current.wordTable.containsKey(ch))
- {
- PrefixTree next = new PrefixTree();
- current.wordTable.put(ch, next);
- current = next;
- }
- else
- {
- current = (PrefixTree) current.wordTable.get(ch);
- }
- }
- /* 设置到该节点止为单词的标志 */
- current.isWord = true;
- }
- /**
- * 删除一个单词
- *
- * @param term
- * 要删除的单词
- */
- public void delete(String term)
- {
- if (null == term)
- {
- return;
- }
- /* 将单词转化为字符串处理 */
- byte[] termChars = term.getBytes();
- PrefixTree current = this;
- PrefixTree fromDelete = this;
- PrefixTree next = this;
- int indexDelete = 0;
- int index = 0;
- for (index = 0; index < termChars.length; ++index)
- {
- if (!current.wordTable.containsKey(termChars[index]))
- {
- return;
- }
- else
- {
- current = (PrefixTree) current.wordTable.get(termChars[index]);
- }
- /* 记录要删除的节点位置,注意不能到最后才判断,因为要删除这个单词最后的节点的isWord肯定被转为了true */
- if (current.isWord && index != termChars.length - 1)
- {
- fromDelete = current;
- indexDelete = index + 1;
- }
- }
- /* 有后缀节点,说明被其他单词共享了前缀 */
- if (current.wordTable.size() != 0)
- {
- current.isWord = false;
- return;
- }
- /* 删除所有不共享的后缀节点 */
- for (current = fromDelete, index = indexDelete; index < termChars.length; ++index)
- {
- next = current.wordTable.get(termChars[index]);
- current.wordTable.remove(termChars[index]);
- current = next;
- current.isWord = false;
- }
- }
- /**
- * 获取提示的词条
- *
- * @param preTerm
- * 用户已输入的词条
- * @return 提示词条
- */
- public List<String> promptsTerms(String preTerm)
- {
- if (null == preTerm)
- {
- return null;
- }
- PrefixTree current = this;
- List<String> words = new ArrayList<String>();
- Stack<Byte> stack = new Stack<Byte>();
- /* 将单词转化为字符串处理 */
- byte[] termChars = preTerm.getBytes();
- for (byte ch : termChars)
- {
- if (!current.wordTable.containsKey(ch))
- {
- return null;
- }
- else
- {
- current = (PrefixTree) current.wordTable.get(ch);
- }
- }
- /* 如果本身输入的已经是一个单词了,需要先记录,由于后面处理时要加前缀,这个地方只给个空串即可 */
- if (current.isWord)
- {
- words.add("");
- }
- /* 递归地遍历,寻找所有匹配的单词 */
- this.traversalTree(current, stack, words);
- /* 将每个单词都加上前缀 */
- for (int index = 0; index < words.size(); ++index)
- {
- words.set(index, preTerm + words.get(index));
- }
- return words;
- }
- /**
- * 递归生成所有后缀的单词
- *
- * @param current
- * 当前的根节点
- * @param words
- * 单词列表
- */
- private void traversalTree(PrefixTree current, Stack<Byte> stack,
- List<String> words)
- {
- for (byte ch : current.wordTable.keySet())
- {
- stack.push(ch);
- /* 遇到是单词的标志 */
- if (current.wordTable.get(ch).isWord)
- {
- /* 记录单词 */
- words.add(stackToWord(stack));
- }
- /* 如果还有后续节点则递归 */
- if (current.wordTable.get(ch).wordTable.size() != 0)
- {
- this.traversalTree(current.wordTable.get(ch), stack, words);
- }
- stack.pop();
- }
- }
- /**
- * 输出从栈底到栈顶的一条路径,即为一个单词
- *
- * @param stack
- * 栈
- * @return 栈中所存储的单词
- */
- private String stackToWord(Stack<Byte> stack)
- {
- String word = "";
- for (byte ch : stack)
- {
- word += (char) ch;
- }
- return word;
- }
- }
- package com.algorithm;
- import java.util.List;
- /**
- * 测试代码
- *
- * E-mail:530025983@qq.com 2011-5-28
- *
- * @author MONKEY_D_MENG
- *
- */
- public class Main
- {
- public static void main(String[] args)
- {
- PrefixTree prefixTree = new PrefixTree();
- List<String> words;
- prefixTree.insert("mon");
- prefixTree.insert("money");
- prefixTree.insert("monkey");
- prefixTree.insert("monkey1");
- prefixTree.insert("monkey2");
- prefixTree.insert("monkey3");
- prefixTree.insert("monkey4");
- prefixTree.insert("monkey5");
- prefixTree.delete("money");
- words = prefixTree.promptsTerms("mon");
- for (String word : words)
- {
- System.out.println(word);
- }
- }
- }
本文转自莫水千流博客园博客,原文链接:http://www.cnblogs.com/zhoug2020/p/3320221.html,如需转载请自行联系原作者