如何从二叉搜索树中随机均匀地返回节点?

时间:2014-09-19 20:11:53

标签: algorithm binary-search-tree probability traversal

给定BST(可能或可能不平衡)如何随机均匀地返回“任意”节点?约束是您不能使用外部索引数据结构。您必须以这样一种方式遍历树,即每个节点都有相同的访问机会。

这个问题困扰了我很长一段时间。如果我们确实可以使用外部哈希表/指针,我们可以随机化它们并返回相应的节点。但是,我的同事提出了一个相当复杂的问题变体,其中不能使用其他数据结构。

  • 由于返回树底部附近节点的概率要小得多(概率复合),因此一个简单的随机游走有50%的可能性去L / R不起作用
  • 即使是随机生成深度d并且遍历最多d个节点以返回节点(或者如果它是叶子则停止)也不会生成均匀分布。

更新:您不能进行顺序遍历并将结果存储在数组中。

如何实现这样的遍历?

3 个答案:

答案 0 :(得分:4)

以任何顺序走树,保留以下值:

  • N:看到的节点数

  • selected:当前选定的节点。

最初,N为0,selectedNone。访问节点包括以下内容:

  1. 增量N

  2. 生成[0, N)范围内的随机整数。

  3. 如果选择的随机整数为0,请将selected设置为当前节点。

  4. 请注意,在行走期间需要修改值Nselected。这意味着它们都是访问者函数的输入和输出值。

    在漫游结束时,N将是树中节点的数量,selected将是以均匀概率选择的随机节点(假设您有一个好的随机数生成器)

    此算法不限于BST。它适用于任何形状的任何树。特别是,它将处理一个简单的未知长度的线性序列,对应于众所周知的随机选择算法,该算法将迭代对象,用新概率的随机对象替换所选择的随机对象{{1}其中1/N是迄今为止看到的对象数。

    如果您跟踪被访问的节点,它也可以在任何连接的图上工作。

    如果您有一个非常大的树(或图形),可能分布在许多服务器和/或存储设备上,您可以使用此算法的不同表示,它提供一定程度的并行性(并且还可以防止需要保持全局遍历结构或将值传递给walk)。

    我们假设每个节点服务器都可以直接访问N个对象,并可以间接访问一些已知数量的子服务器。该算法允许冗余的孩子,但假设网络通信(几乎)完美;处理网络分裂超出了本答案的范围。我们还假设每个查询都有一个关联的唯一查询号,这允许我们处理一些网络工件。查询没有其他信息(除了要响应的服务器),并且应该返回由计数和随机选择的节点组成的元组。

    当节点服务器收到标识为k的查询时,它会执行以下操作:

    1. 如果之前已对查询q做出回复,请立即返回q

    2. <0, null>设置为count,将k设置为可直接访问的selected个对象中随机选择的对象。

    3. 对于每个子服务器,发送查询(具有相同的查询ID)

    4. 对于每个回复的回复(回复的顺序并不重要):

      一个。将k添加到response.count

      湾概率count,将response.count / count替换为selected

    5. 当所有子级服务器都已响应时,请返回response.selected

答案 1 :(得分:1)

如果知道树中节点数n,则在树的顺序遍历中找到第k个节点,用于在0和n-1之间随机选择的k,包括0和n-1。如果你没有,你可以走树并找出它的大小,然后再做上面的事情。

如果树的节点连续存储在某个数组中,则随机采样数组元素并将其返回。

如果每个树节点都可以告诉你那里生根的子树的大小,找出树中有多少个节点,生成一个随机k到树中的节点数,然后选择树的第k个元素

在上述所有情况下,&#34;树&#34;部分是红鲱鱼。

在任何非平凡的连通图上都有随机游走,其静态分布是均匀的。选择当前节点的邻居。如果它的程度较低或相同,那就去吧。如果它具有更高的学位,那么以概率cur_deg / that_deg去那里,否则保持不变。这种随机游走的采样在各种情况下被调用&#34;吉布斯采样&#34;和#34; Metropolis-Hastings。&#34;

答案 2 :(得分:0)

如果你有一个完整的(底层是完全满的)二分查找树,那么就可以快速(子线性时间)按你的要求去做,因为你知道树的结构。如果您希望我发布给出此案例解决方案的答案,请告诉我,我会更新我的答案。但是,如果你只有一个任意形状的通用二叉搜索树,那么就不可能在不访问整个树的情况下统一采样节点,这样你就可以知道它的形状了。