给定BST(可能或可能不平衡)如何随机均匀地返回“任意”节点?约束是您不能使用外部索引数据结构。您必须以这样一种方式遍历树,即每个节点都有相同的访问机会。
这个问题困扰了我很长一段时间。如果我们确实可以使用外部哈希表/指针,我们可以随机化它们并返回相应的节点。但是,我的同事提出了一个相当复杂的问题变体,其中不能使用其他数据结构。
d
并且遍历最多d
个节点以返回节点(或者如果它是叶子则停止)也不会生成均匀分布。更新:您不能进行顺序遍历并将结果存储在数组中。
如何实现这样的遍历?
答案 0 :(得分:4)
以任何顺序走树,保留以下值:
N
:看到的节点数
selected
:当前选定的节点。
最初,N
为0,selected
为None
。访问节点包括以下内容:
增量N
生成[0, N)
范围内的随机整数。
如果选择的随机整数为0,请将selected
设置为当前节点。
请注意,在行走期间需要修改值N
和selected
。这意味着它们都是访问者函数的输入和输出值。
在漫游结束时,N
将是树中节点的数量,selected
将是以均匀概率选择的随机节点(假设您有一个好的随机数生成器)
此算法不限于BST。它适用于任何形状的任何树。特别是,它将处理一个简单的未知长度的线性序列,对应于众所周知的随机选择算法,该算法将迭代对象,用新概率的随机对象替换所选择的随机对象{{1}其中1/N
是迄今为止看到的对象数。
如果您跟踪被访问的节点,它也可以在任何连接的图上工作。
如果您有一个非常大的树(或图形),可能分布在许多服务器和/或存储设备上,您可以使用此算法的不同表示,它提供一定程度的并行性(并且还可以防止需要保持全局遍历结构或将值传递给walk)。
我们假设每个节点服务器都可以直接访问N
个对象,并可以间接访问一些已知数量的子服务器。该算法允许冗余的孩子,但假设网络通信(几乎)完美;处理网络分裂超出了本答案的范围。我们还假设每个查询都有一个关联的唯一查询号,这允许我们处理一些网络工件。查询没有其他信息(除了要响应的服务器),并且应该返回由计数和随机选择的节点组成的元组。
当节点服务器收到标识为k
的查询时,它会执行以下操作:
如果之前已对查询q
做出回复,请立即返回q
将<0, null>
设置为count
,将k
设置为可直接访问的selected
个对象中随机选择的对象。
对于每个子服务器,发送查询(具有相同的查询ID)
对于每个回复的回复(回复的顺序并不重要):
一个。将k
添加到response.count
湾概率count
,将response.count / count
替换为selected
当所有子级服务器都已响应时,请返回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)
如果你有一个完整的(底层是完全满的)二分查找树,那么就可以快速(子线性时间)按你的要求去做,因为你知道树的结构。如果您希望我发布给出此案例解决方案的答案,请告诉我,我会更新我的答案。但是,如果你只有一个任意形状的通用二叉搜索树,那么就不可能在不访问整个树的情况下统一采样节点,这样你就可以知道它的形状了。