【问题标题】:How to return a node, uniformly at random, from a binary search tree?如何从二叉搜索树中均匀地随机返回一个节点?
【发布时间】:2014-09-19 20:11:53
【问题描述】:

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

这个问题让我困惑了好一阵子。如果我们确实可以使用外部哈希表/指针,我们可以对它们进行随机化并返回相应的节点。但是,我的同事提出了这个问题的一个相当复杂的变体,其中不能使用额外的数据结构。

  • 具有 50-50 机会进行 L/R 的简单随机游走不起作用,因为返回靠近树底部的节点的概率要小得多(概率复合)
  • 即使随机生成深度 d 并遍历最多 d 节点以返回节点(或者如果它是叶节点则停止)也不会生成均匀分布。

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

如何实现这样的遍历?

【问题讨论】:

  • 你能不能只生成一个介于 0 和树中节点数之间的随机数,然后按顺序遍历树,这样一旦你从一个节点移动到另一个“随机数”数量有时,您会停在那个节点并返回?这似乎太简单了,所以也许这不是你要找的:/
  • 没有建议将任何内容存储在数组中
  • 如果一个有序的工作是不允许的,那么这个问题是可笑的
  • 是的,在一个普通的旧二叉搜索树中均匀采样而不访问每个节点是完全不可能的。
  • @DavidEisenstat:这可能不是该禁令中“和”的意思。无论如何,显然您必须访问每个节点(除非您事先知道有多少个节点),但您不必按顺序访问它们。

标签: algorithm binary-search-tree probability traversal


【解决方案1】:

以任意顺序遍历树,保持以下值:

  • N: 看到的节点数

  • selected:当前选中的节点。

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

  1. 递增N

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

  3. 如果选择的随机整数为0,则设置selected为当前Node。

请注意,Nselected 的值需要在遍历过程中进行修改。这意味着它们既是访问者函数的输入值,也是输出值。

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

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

如果您跟踪访问过的节点,它也适用于任何连通图。

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

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

当节点服务器接收到 ID 为 q 的查询时,它会执行以下操作:

  1. 如果之前回复过查询q,立即返回<0, null>

  2. count 设置为k,并将selected 设置为从它可以直接访问的k 对象中随机选择的对象。

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

  4. 对于每个返回的响应(响应的顺序无关紧要):

    一个。将response.count添加到count

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

  5. 当所有子服务器都响应后,返回<count, selected>

【讨论】:

  • 不错的算法,值得一提的是您正在使用 Reservoir Sampling。 :)
  • @Tuxdude:或者主题的变体,在并行多服务器算法的情况下。
  • @rici 步骤“在 [0, N) 范围内生成一个随机整数”是否有可能永远不会生成 0?在这种情况下,selected 将始终为 null/none。
  • @codewarrior:不。第一次执行第2步,N1,“[0, N)范围内的随机整数”只能为0
【解决方案2】:

如果您知道树中节点 n 的数量,请在树的中序遍历中找到第 k 个节点,该节点在 0 和 n-1 之间随机选择一个 k,包括在内。如果你不这样做,你可以走这棵树并计算出它的大小,然后执行上述操作。

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

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

在上述所有情况下,“树”部分都是红鲱鱼。

在任何平稳分布均匀的非平凡连通图上都存在随机游走。选择当前节点的邻居。如果它的程度较低或相等,请去那里。如果它具有更高的学位,则以概率 cur_deg / that_deg 去那里,否则就留在原地。这种随机游走的采样在各种上下文中称为“Gibbs 采样”和“Metropolis-Hastings”。

【讨论】:

【解决方案3】:

如果你有一个完整的(底层完全完整的)二叉搜索树,那么你可以快速(亚线性时间)按照你的要求去做,因为你知道树的结构。如果您希望我发布一个为这种情况提供解决方案的答案,请告诉我,我会更新我的答案。但是,如果你只有一个任意形状的通用二叉搜索树,那么在不访问整棵树的情况下就不可能对一个节点进行均匀采样以便知道形状。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-08-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-10-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多