Algorithm 查找字数的最快方法,从指定的字符串开始(字数存储在AVL树中)
我已经实现了自己的AVL树,并将其用作字典。我在想,什么是计算所有以字符串开头的单词的最快方法 例如: 我已经开始工作了,但是,我听说可以做得更快。Algorithm 查找字数的最快方法,从指定的字符串开始(字数存储在AVL树中),algorithm,avl-tree,Algorithm,Avl Tree,我已经实现了自己的AVL树,并将其用作字典。我在想,什么是计算所有以字符串开头的单词的最快方法 例如: 我已经开始工作了,但是,我听说可以做得更快。 当然,我可以在节点中保存其他信息,例如下面的节点和其他类似的信息。如果您想要更改数据结构,您可以从一个数据库中获得优异的性能。如果trie包含静态数据,则可以通过使用动态编程生成的子树计数大小来注释分支,从而获得更好的性能 e、 g代表[竖琴,帽子,嗨] h(3) a(2) i() r(1) t(
当然,我可以在节点中保存其他信息,例如下面的节点和其他类似的信息。如果您想要更改数据结构,您可以从一个数据库中获得优异的性能。如果trie包含静态数据,则可以通过使用动态编程生成的子树计数大小来注释分支,从而获得更好的性能 e、 g代表[竖琴,帽子,嗨]
h(3)
a(2) i()
r(1) t()
p()
如果您打算更改数据结构,您可以从一个数据库中获得优异的性能。如果trie包含静态数据,则可以通过使用动态编程生成的子树计数大小来注释分支,从而获得更好的性能 e、 g代表[竖琴,帽子,嗨]
h(3)
a(2) i()
r(1) t()
p()
可以修改AVL树,以便每个节点也知道其索引1。如果集合是排序数组,则索引是元素编号 您现在需要做的就是: 搜索FA,得到最接近但更大或相等的索引i1 在树中为它添加元素 搜索FB,获取树中最接近但较小的元素的索引i2。 找出i1和i2之间存在差异的元素数量,区分在1中发现FA的情况和未发现FA的情况。 这两个1,2都是Ologn-3是常量,因此总复杂度实际上是Ologn*| S |,因为每个比较都是O | S |本身,并且有Ologn比较
1这是通过让每个节点记住它有多少子节点来完成的,您可以使用此信息最终提取索引。可以修改AVL树,以便每个节点也知道其索引1。如果集合是排序数组,则索引是元素号 您现在需要做的就是: 搜索FA,得到最接近但更大或相等的索引i1 在树中为它添加元素 搜索FB,获取树中最接近但较小的元素的索引i2。 找出i1和i2之间存在差异的元素数量,区分在1中发现FA的情况和未发现FA的情况。 这两个1,2都是Ologn-3是常量,因此总复杂度实际上是Ologn*| S |,因为每个比较都是O | S |本身,并且有Ologn比较
1这是通过让每个节点记住它有多少子节点来完成的,您可以使用此信息最终提取索引。如果您希望在保持相同的渐近时间界限的同时尽可能减少内存占用,您可以满足每个节点一个整数的要求,并且仍然可以实现Olog n time(假定时间键比较为常数) 与每个节点一起存储其子树的大小。这可以在树修改期间轻松更新 要查找具有给定范围的键数,请执行以下操作: 查找此范围内的顶部元素。也就是说,位于范围内但其祖先节点均不在范围内的唯一节点。调用元素top。 如果不存在这样的元素,则返回0 初始化sum=1表示顶部。 在top的左子树中查找范围的起点: 如果从节点向左下降,则将其整个右子树的大小添加到总和,然后添加一个。 如果向右下降,则不添加任何内容。 在top的右子树中查找范围的结尾: 如果从节点向右下降,则将其整个左子树的大小添加到总和,然后添加一个。 如果向左下降,则不添加任何内容。 返回总数。 给定前缀的范围包含具有该前缀的所有元素。需要注意的是,具有给定前缀的字符串集是连续的w.r.t。它的排序顺序——也就是说,它确实是一个范围 前缀范围的起点是前缀本身之前的位置 前缀范围的末尾是在这一个FA=>FB之后的第一个不相交前缀之前的位置;FZ=>GA,当字母表中只有A-Z时
Unicode通过引入一个实际上可能不会出现在文本中的“top”字符来简化这一过程,并将其与所有其他字符进行比较。也就是说,end=prefix+\uFFFF。如果您希望在保持相同的渐近时间界限的同时尽可能减少内存占用,那么每个节点只需一个整数就足够了,并且在假设恒定时间键比较的情况下仍然可以实现Olog n time 与每个节点一起存储其子树的大小。这可以在树修改期间轻松更新 要查找具有给定范围的键数,请执行以下操作: 查找此范围内的顶部元素。也就是说,位于范围内但其祖先节点均不在范围内的唯一节点。调用元素top。 如果不存在这样的元素,则返回0 初始化sum=1表示顶部。 在top的左子树中查找范围的起点: 如果从节点向左下降,请添加其整个右侧子节点的大小 将树添加到总和,然后添加一个。 如果向右下降,则不添加任何内容。 在top的右子树中查找范围的结尾: 如果从节点向右下降,则将其整个左子树的大小添加到总和,然后添加一个。 如果向左下降,则不添加任何内容。 返回总数。 给定前缀的范围包含具有该前缀的所有元素。需要注意的是,具有给定前缀的字符串集是连续的w.r.t。它的排序顺序——也就是说,它确实是一个范围 前缀范围的起点是前缀本身之前的位置 前缀范围的末尾是在这一个FA=>FB之后的第一个不相交前缀之前的位置;FZ=>GA,当字母表中只有A-Z时
Unicode通过引入一个实际上可能不会出现在文本中的“top”字符来简化这一过程,并将其与所有其他字符进行比较。也就是说,end=prefix+\uFFFF。AVL树不是实现所需的正确数据结构。还有另一种称为基数树的数据结构,它可以回答Olg N复杂度的前缀计数查询。基数树中的每个节点n都有0到26个子节点。它还有一个辅助变量prefix_count,它告诉我们基数树中有多少单词以前缀n开头。例如,这里是单词abbaba和abbacba的根树 prefix_count函数的实现类似于插入过程
prefix-count( rt, x ) :
cn := 1; // root
foreach i in x :
if rt.array[ cn ].child[ i ] = null :
return 0; // 0 prefixes found
else :
cn := rt.array[ cn ].child[ i ];
return rt.array[ cn ].pc;
AVL树不是实现所需的正确数据结构。还有另一种称为基数树的数据结构,它可以回答Olg N复杂度的前缀计数查询。基数树中的每个节点n都有0到26个子节点。它还有一个辅助变量prefix_count,它告诉我们基数树中有多少单词以前缀n开头。例如,这里是单词abbaba和abbacba的根树 prefix_count函数的实现类似于插入过程
prefix-count( rt, x ) :
cn := 1; // root
foreach i in x :
if rt.array[ cn ].child[ i ] = null :
return 0; // 0 prefixes found
else :
cn := rt.array[ cn ].child[ i ];
return rt.array[ cn ].pc;
你能储存多少信息?如果你储存的足够多,它在Olog n中是微不足道的。如果你知道O1,在Olog n时间内仍然有可能。到目前为止,我正在寻找第一个以'fa'开头的单词,这是Olog n,然后我计算示例中的所有fak,然后从这里开始递归函数,找到这些单词。我可以存储很多数据。你可以通过在每个节点中只存储一个以上的整数来实现Olog n。这样的整数包含什么?它的子树的大小-这足以一次性获得Olog n。你可以存储多少信息?如果你储存的足够多,它在Olog n中是微不足道的。如果你知道O1,在Olog n时间内仍然有可能。到目前为止,我正在寻找第一个以'fa'开头的单词,这是Olog n,然后我计算示例中的所有fak,然后从这里开始递归函数,找到这些单词。我可以存储很多数据。您可以通过在每个节点中只存储一个以上的整数来实现Olog n。这样的整数会包含什么?它的子树的大小-这足以获得Olog n时间。如果集合不是按数组排序的呢?我是从随机化器中得到这些单词的。在插入和删除过程中,索引很难更新。@Patryk:我的意思是:假设FAB在集合中。现在假设您有另一个具有相同内容的排序数组,数组[i]==FAB,然后indexFAB==i。实际上,你并不需要数组,它只是解释索引是什么的一种方式。@JanDvorak不需要,它只是一种添加到卷中的东西,几年前我作为一个非常精确的人做过它,我不记得我花了太长时间才将此功能添加到标准AVL树中。@amit那么我认为我们描述的是相同的算法,只是您从根搜索两个边界。如果集合不是排序数组呢?我是从随机化器中得到这些单词的。在插入和删除过程中,索引很难更新。@Patryk:我的意思是:假设FAB在集合中。现在假设您有另一个具有相同内容的排序数组,数组[i]==FAB,然后indexFAB==i。实际上,你并不需要数组,它只是解释索引是什么的一种方式。@JanDvorak不需要,它只是一种添加到卷中的东西,几年前我作为一个非常精确的人做过它,我不记得我花了太长时间才将此功能添加到标准AVL树。@amit那么我认为我们描述的是相同的算法,只是从根开始搜索两个边界
struct node :
let child -> array of 26 characters;
let pc -> integer prefix counter;
struct radix-tree :
node array[ MAXN ];
let size -> integer size of the trie
init( rt ) :
size := 1; // add the root
insert( rt, x ) :
cn := 1; // root
foreach i in x :
if rt.array[ cn ].child[ i ] = null : // node doesn't exist
rt.array[ cn ].child[ i ] := ++rt.size;
cn := rt.size;
else : // node exists
cn := rt.array[ cn ].child[ i ];
tr.array[ cn ].pc += 1;
prefix-count( rt, x ) :
cn := 1; // root
foreach i in x :
if rt.array[ cn ].child[ i ] = null :
return 0; // 0 prefixes found
else :
cn := rt.array[ cn ].child[ i ];
return rt.array[ cn ].pc;