但是我几天前参加考试时遇到了挑战。
如果我们使用n
个顶点平衡二叉树,并在子树中为每个节点存储子节点数(即该节点作为根节点的子节点数)。我们如何在O(log n)
中执行这些操作?
1)找到给定元素的顺序。 (即像OS-SELECT(x,i)中的顺序统计树,它找到了以x为根的子树中的第一个最小元素,或者为了更好地理解链接上的第303页CLRS Book,但在本书中作者说这个操作在红黑树上不是AVL)
2)找到给定a
和b
之间的数字(假设为a < b
)。
答案 0 :(得分:2)
1)您要求二叉树中的第N个最小元素。我们需要找到元素,但是我们需要存储更小元素的数量。
每当我们走向正确时,我们都需要计算“我们通过的节点”。例如,我们从根开始然后向右走,然后我们需要在根的左子树中查找子节点的数量,并将其添加到我们当前的“订单计数”。
从变量order
开始并将其设置为1.我们正在搜索元素N
。
while not at N:
if current == N
//Need to add the number of left children of N to count.
count += N.LeftSubTreeCount
return count
if current > N
//We are going left.
update current
else
//We are going right.
count += current.LeftSubTreeCount + 1
update current
此解决方案的时间复杂度为O(log n),因为我们只是在每个步骤中搜索树中具有恒定开销的元素。在平衡二叉树中搜索元素的时间是最坏情况O(log n)。
2)这个最简单的解决方案是利用1)中的功能。但是,由于我们在某个时间间隔内搜索树中的元素数量,我们首先需要找到largest
元素,即&lt; = a,将其称为aClosest,smallest
元素是&gt; = b,bClosest。
在我们找到这些之后,我们可以在树中计算他们的顺序并计算:order(bClosest) - order(aClosest)
。在1)中开发的函数的顺序。
此解决方案的时间复杂度也是O(log n)。我们所做的只是寻找元素。我们必须总共搜索4次。我们两次搜索我们想要计算顺序的元素,两次我们在这些元素上运行1)中的函数。然后总复杂度为O(4 * log n) - > O(log n)。如果我们可以修改搜索并在同一次迭代中计算顺序,则可以减少这种情况。
搜索最大元素&lt; = a时,您将需要搜索a。然后会发生两件事:
如果是1.那么你很好。但是对于2.你可以通过存储你在路径上遇到的最高值来处理。一个。如果a不在树中,那将是您正在寻找的元素。此搜索的输出是aClosest。
类似地,当搜索b时,存储最小数字的值&gt;湾
答案 1 :(得分:2)
如何找到一个给定元素的顺序?
元素的顺序是它在生成的排序序列上的排名。
如果假设我们在子树中存储子项数,我们可以将 虚拟重量 分配给边。任何边缘向左移动0
。任何边缘(x -> Y
)向右加权x +1左侧树中的子项数,即count(left(x))+1
。
现在我们可以修改搜索算法
find(x, node)
if node is null return error
if node is x return node
if node < x return find(x, left(node)) else return find(x, right(node))
通过添加累加器,最初为0
order(x, node, acc)
if node is null return acc
if node is x return acc + count(left(x))
if node < x return order(x, left(node), acc)
else return order(x, right(node) , acc + 1 + count(left(node)) )
因为额外的存储count(x)
是恒定时间。哪个使rank
算法O(log n)
找到给定a和b之间的数字(假设a&lt; b)。
order(b) - order(a) + 1
O(log(n))
中无法实现。提供证明您只需选择最左边的元素为a
,最右边的元素为b
,它们之间有n-1
个元素。答案 2 :(得分:1)
对于问题1),您需要创建一个dfs(按顺序遍历)。每次访问节点count++
直到找到给定元素时,count
都是给定元素的顺序。所以需要 O(n)来查找给定元素的顺序。对于排序数组,我们可以使用二进制搜索在 O(log n)中轻松找到它的顺序,但是对于BST,我们不能这样做,除非向树节点添加其他属性。
<强>更新强> 我很抱歉没有注意到在搜索之前知道孩子的数量。使用此属性,无需穿越树。你只需要在搜索时计算少于给定元素的节点数。那就是每次去正确的孩子,
count += number(left children) + 1;
然后count的结果是给定元素的顺序。搜索函数的这种修改需要每个函数调用一次,它不会影响搜索的时间复杂度, O(log n)
对于问题2),它与在树中查找给定元素但条件松散相同。您只需修改搜索功能:将if(current_element == given_element) found;
更改为if(a < current_element < b) found;
。所以时间复杂度与普通搜索相同, O(log n)