想知道最有效的方法是将二进制搜索树变成拼写检查器,方法是读入1000字的字典文件,然后检查另一个说有几段的文档。
答案 0 :(得分:8)
三元树trie会更有效率
答案 1 :(得分:3)
您是否已使用二叉搜索树? Bloom filter可能是一种更有效的数据结构。
答案 2 :(得分:1)
如果您只是想查看字典中是否存在某个特定单词(即拼写正确),那么我认为二元搜索树不是您所追求的。存储该信息的更好方法是树形式,其中树上的每个连续节点都是一个字符,并且读取结束节点的路径会为您提供该单词的拼写。您还需要添加一个标记来表示单词结尾。
例如:说你的字典有这些词:汽车,购物车,猫,杯子,切割
- C
- A
- R
- end
- T
- T
- end
- U
- P
- end
- T
- end
检查单词是否存在是一个单独查看每个字母的问题,并且它存在于当前节点的子节点中。
Check for "cat"
Does "C" exist at the root level? Yes, move to the next letter.
Does "A" exist underneath C? Yes, move on.
Does "T" exist underneath A? Yes, move on.
Is there a word ending after the T? Yes. Word exists.
Check for "cu"
Does "C" exist at the root level? Yes, move to the next letter.
Does "U" exist at the root level? Yes, move to the next letter.
Is there a word ending after the U? No. Word does not exist.
您如何存储此信息取决于您。正如Steven指出的那样,Ternary Search Trie可能是要走的路:每个节点都有27个可能的子节点。
答案 3 :(得分:1)
This网站应该可以帮助您 它在java中实现。
答案 4 :(得分:0)
如果您还需要进行自动建议/前缀搜索,那么值得查看patricia树或基数树。
答案 5 :(得分:0)
根据您提供的示例,性能可能无关紧要,因为在PC上,如果您不使用,则整个操作将花费大约1%的时间用户阅读您显示的第一个结果。完全愚蠢的算法。但是,我仍然认为这个问题非常严重,以至于性能问题。
如果字典文件是预分类的(大多数是),并且如果文本相对于你描述的字典相对较小,那么我会非常想对文本进行排序,可能会删除重复项,然后遍历两个列表并排使用与合并排序相同的过程,除了您报告每个文本单词是否在字典中而不是输出合并列表。
这完成了关于排序的M log M比较的工作,加上迭代的大多数N + M比较(可能更少,但不复杂 - 更少)。这与一次性操作的最佳复杂性相当接近:要摆脱N中的线性项,您需要找到不从磁盘读取整个字典的方法。我很确定可以对文件进行研究,特别是考虑到单词很短,但是对于小N,任何人都猜测,寻找这个地方实际上是否比串行访问数据更快。
它具有以下特征:
当然,如果字典文件没有预先排序,那么这不起作用,如果您可以将字典保留在内存中以进行下一次拼写检查操作,那么您可以分摊I / O的成本和将它处理成几个不同文本的树,从长远来看,这将是一场胜利。
如果字典非常庞大,那么您可能会受益于以预处理的形式将其存储在磁盘上,这种形式相当于根据您语言中各种单词的相对频率加权的非平衡树。然后,您可以为小文本执行少于O(N)磁盘访问,并且在大多数操作系统上根本不打算将其加载到内存中,只需mmap文件并让操作系统担心它。对于大型词典,不需要触摸包含以“二甲”开头的单词的整个群集。
另一个考虑因素是字典的splay树。当您在其中查找内容时,展开树会自行失衡,以便更快地查找常用值。大多数文本反复使用少量单词,因此如果文本足够长以证明开销合理,那么最终将获胜。
上述两个都受到Steven A Lowe的观点,即对于弦乐来说,trie击败了普通的树。不过,不知道你是否会找到一个现成的画面。
答案 6 :(得分:0)
看到这是一个家庭作业问题,我将假设您必须使用普通的旧二叉树(没有红黑树,AVL树,基数树等)。然后答案是在从单词列表构建树时尝试保持树的平衡。一种方法是在读取之前随机化列表,这给出了合理的结果。但是如果您对输入序列进行排序(使用与树使用的相同的比较),则可以获得更好的结果,然后递归细分返回中点的输入,直到没有元素保留。结果是一棵平衡的树。
我在C#中尝试了三种不同的方法:
private static IEnumerable<T> BinaryTreeOrder<T>(IList<T> range, int first, int last)
{
if (first > last)
{
yield break;
}
int mid = (first + last) / 2;
yield return range[mid];
foreach (var item in BinaryTreeOrder(range, first, mid - 1))
{
yield return item;
}
foreach (var item in BinaryTreeOrder(range, mid + 1, last))
{
yield return item;
}
}
private static void BinaryTreeOrder<T>(IList<T> range, int first, int last,
ref IList<T> outList)
{
if (first > last)
{
return;
}
int mid = (first + last) / 2;
outList.Add(range[mid]);
BinaryTreeOrder(range, first, mid - 1, ref outList);
BinaryTreeOrder(range, mid + 1, last, ref outList);
}
private static void BinaryTreeOrder<T>(IList<T> range, int first, int last,
ref BinaryTree<T> tree) where T : IComparable<T>
{
if (first > last)
{
return;
}
int mid = (first + last) / 2;
tree.Add(range[mid]);
BinaryTreeOrder(range, first, mid - 1, ref tree);
BinaryTreeOrder(range, mid + 1, last, ref tree);
}
答案 7 :(得分:0)
正如所建议的那样,trie比二叉树更有效,但你可以使用hashmap并散列每个单词。你有一个小字典(1000个条目)。在遍历文档时,检查单词是否在hashmap中。如果他们不是,则假定该单词拼写错误。
这不会让你对拼写错误的单词进行修正。它只是告诉你是或否(正确与否)。
如果您想要对不正确的单词进行拼写建议,可以从文件中的单词开始,然后生成1个编辑距离的所有单词,并将这些作为初始单词的子项添加。这样您就可以构建图形了。深入2级以获得最大速度与准确度。如果生成字典中的单词节点,则可以将其添加到可能的建议列表中。最后,返回可能的建议列表。
为了更好的拼写检查,还要尝试添加拼音匹配。
sea yuh - &gt;见呀
这种方法(创建字符串1编辑图表)是&#34;慢&#34;。但这是一个很好的学术活动。运行时为O(n ^分支)。
如果对此感兴趣的是我自己构建的一个链接(为了好玩):https://github.com/eamocanu/spellcheck.graph
一些示例图表:https://github.com/eamocanu/spellcheck.graph/tree/master/graph%20photos
我还为其添加了一个生成图形的UI组件。这是一个外部库。