python - 生成一个带有n个顶点

时间:2017-11-15 12:51:05

标签: python random binary-tree

我正在调试一个算法任务,为了测试的目的,我想生成一个二进制树,其中n个顶点以0为根,换句话说,生成一对对,这样如果序列中的第i对是(a,b),那么第i个节点的左子节点是a,右子节点是b,除非a(或b)= -1,那么第i个节点没有左(或右)子节点。 (例如,序列:(1,2),( - 1,-1),( - 1,-1)是具有根(0)的树,以及两个子(1和2)) 我写了下面的python递归函数,其中listAdd是所需的对列表,listDel是尚未生成的所有顶点,n是当前正在查看的节点:

 
def generateTree(listAdd, listDel, n):
    if not listDel:
        return
    ifLeft = bool(randint(0,1))
    ifRight = bool(randint(0,1))
    if ifLeft:
        chosen = choice(listDel)
        listDel.remove(chosen)
        listAdd[n] = (chosen, listAdd[n][1])
        generateTree(listAdd, listDel, chosen)
    else:
        listAdd[n] = (-1, listAdd[n][1])
    if not listDel:
        return
    if ifRight:
        chosen = choice(listDel)
        listDel.remove(chosen)
        listAdd[n] = (listAdd[n][0], chosen)
        generateTree(listAdd, listDel, chosen)
    else:
        listAdd[n] = (listAdd[n][0], -1)

然而,在编写它时,我注意到这不能正常工作,因为如果ifLeft和ifRight都是假的,那么函数可能只在一个顶点之后停止。

所以我的问题是,生成这样一棵树的好方法是什么?我不需要它在python中,因为它只用于生成输入文件,我只需要一个好的算法,或者另一种表示树的方法,这样它就更容易生成并可以转换成所需的格式。

2 个答案:

答案 0 :(得分:1)

一种简单的迭代方法:

import random

# start with root node and no children
tree = [[-1, -1]]
free_edges = [(0, 0), (0, 1)]

n = 4  # how many nodes do you want?

while len(tree) < n:
    e = random.choice(free_edges)  # select a free edge
    node, child = e
    assert tree[node][child] == -1  # make sure we made no mistake

    k = len(tree)  # index of new node
    tree.append([-1, -1])  # add new node

    tree[node][child] = k  # set new node as child of an old node
    free_edges.extend([(k, 0), (k, 1)])  # new node has two free edges

    free_edges.remove(e)  # edge is no longer free

print(tree)
# [[1, 2], [-1, 3], [-1, -1], [-1, -1]]

随机将新节点插入树中,直到达到节点总数。

enter image description here 自由边用红色表示。在每个步骤中,随机选择一个自由边。节点位于该边缘,此节点向树中添加两个新的自由边。

此过程不会生成特定的节点顺序。可以先将左孩子或右孩子插入树中。即你可能得到像[(2, 1), (-1, -1), (-1, -1)]这样的序列。如果这是一个问题,可能需要重新排序节点。

我认为这种方法平均会产生比不平衡树更平衡的树,因为较老的边缘被更频繁地考虑。您可以选择不插入新节点,仅以一定概率移除自由边。这应该将趋势转向更深的树木。只需确保在完成之前不要移除所有边缘:)

答案 1 :(得分:0)

当你在树的右端同时仍然有一些自由元素时,你不能得到双零,这是正确的吗?

所以我认为您需要确定何时发生这种情况,并强制重新检查。我能想到两种方法。

  • 如果你给它一个根,那就做一个检查你在哪里的函数。

  • 创建一个数组,并使用01"L""R"填充该数组。如果你有

    • ifLeft == ifRight == 0
    • len(listDel) > 0
    • 检查数组:
      • If "L" not in check_array
      • if len(check_array) == sum(check_array)

重新加载整个东西。

顺便说一句,您必须在函数开头将检查添加到数组中并在结尾处将其删除。所以它的长度将是这样的:

0,1,2,3,4,3,2,3,4,5,4,3,2,1,2,3,2,1,0

check_array = []

def generateTree(listAdd, listDel, n):
    if not listDel:
        return
    ifLeft = bool(randint(0,1))
    ifRight = bool(randint(0,1))

    if (ifLeft + ifRight == 0) and (
        "L" not in checked_array) and (
        len(listDel) > 0):
        // Force a 1, or use randint (you need a while-loop for it)
        ifLeft = 1

    if ifLeft:
        check_array.push("L")
        chosen = choice(listDel)
        listDel.remove(chosen)
        listAdd[n] = (chosen, listAdd[n][1])
        generateTree(listAdd, listDel, chosen)
        check_array = check_array[:-1]
    else:
        listAdd[n] = (-1, listAdd[n][1])
    if not listDel:
        return
    if ifRight:
        check_array.push("R")
        chosen = choice(listDel)
        listDel.remove(chosen)
        listAdd[n] = (listAdd[n][0], chosen)
        generateTree(listAdd, listDel, chosen)
        check_array = check_array[:-1]
    else:
        listAdd[n] = (listAdd[n][0], -1)
相关问题