Python:操纵子树

时间:2009-09-06 19:33:38

标签: python data-structures tree

我是个笨蛋。我要感谢Allen Downey,Jeffrey Elkner和Chris Meyers 以及“我如何像计算机科学家那样思考”。

我正在构建一个受遗传学启发的程序,以生成符合某些问题的方程式。

节点类如下所示:

class Node(object):
    '''
    '''
    def __init__(self, cargo, left=None, right=None):
        self.cargo = cargo
        self.left  = left
        self.right = right
        self.parent = None
        self.branch = None
        self.seq = 0

    def __str__(self):
        return str(self.cargo)

    def copy(self):
        return copy.deepcopy(self)

我有一个Tree类,其中包含一个属性:self.data,它是一个链接的节点系列,形成一个树,我可以遍历它以生成一个方程式。

要执行交叉,我希望能够从Tree的两个实例中交换随机选择的子树。

正在构造self.data时,它会构建一个带有顺序键的字典,将每个节点保存为一个值。其中一个记录如下:

    3: <__main__.Node object at 0x0167B6B0>} 

我认为我很聪明,只需从两个树实例中选择一个节点,并交换各自的父node.leftnode.right值。每个节点在其node.branch属性中记录它是左侧还是右侧。

我不知道如何引用self.data(subnode)来改变它。

两个树实例都必须通过字典中保存的地址访问彼此的节点。

我担心我必须复制并替换每个子树。

任何意见都将不胜感激。

谢谢,

彼得斯图尔特

加拿大纳奈莫

2 个答案:

答案 0 :(得分:2)

不幸的是,您没有向我们提供Tree类,但我们假设它类似于:

class Tree(object):
  def __init__(self):
    self.data = None
    self.nextkey = 0
    self.thedict = {}

在插入新节点时准确更新各种属性。现在,当你谈到“保存在字典中的地址”时,很明显dict的值不是“地址” - 相反,它是一个Node对象(如果你在节点中定义一个特殊的方法__repr__您可能会以更清晰的方式看到它;您所看到的是默认表示形式,用于所有类型未定义或继承__repr__}的Python对象。

因此,在两个不同的树之间交换随机子树只需要小心更新您保留的所有许多冗余信息(并且必须全部同步)。顺便说一下,如果这样的更新是Tree和/或Node的方法,并且可以用于各种“编辑”(插入,删除等)中的任何一种,而不是深埋在执行更新的函数中,那将会更简单作为随机交换的一部分 - 这是良好的OO实践。但是,这有点像是一个侧面问题。

你也没有确切地告诉我们branch属性是如何工作的,我认为它是一个字符串,'左'或'右'(如果没有父,即无根)节点)。

要删除子树,您需要更新:父节点,设置为None其相应的属性;子树的根,设置为None的父和分支属性;和树,从树的thedict属性中删除该条目。您还需要记住父和分支是什么,以便能够在该位置插入一些其他子树。因此...

def removeSubtreeFromTree(tree, keyindict):
  subtreenode = tree.thedict.pop(keyindict)
  parent, branch = subtreenode.parent, subtreenode.branch
  # a sanity chech can't hurt...;-)
  assert getattr(parent, branch) is subtreenode
  subtreenode.parent, subtreenode.branch = None, None
  setattr(parent, branch, None)
  return subtreenode, parent, branch

现在将新子树添加到给定的父树和树中的分支更简单:

def addNewSubtree(tree, subtreenode, parent, branch):
  # sanity checks R us
  assert getattr(parent, branch) is None
  assert subtreenode.parent is None
  assert subtreenode.branch is None
  setattr(parent, branch, subtreenode)
  subtreenode.parent = parent
  subtreenode.branch = branch
  tree.thedict[tree.nextkey] = subtreenode
  tree.nextkey += 1

请注意,您不能只重复使用以前的密钥:可能存在“冲突”(假设密钥仅​​在单个给定树中是唯一的...如果您将它们全局唯一,那么您确实可以重用它们)

最后,将这两个操作放在一起可以完成。如果你永远不需要“交换”一棵树的根,那就更简单了(没有特殊情况需要处理无父子树......)所以我暂时会假设(如果你想要更多的通用性,你将需要编码挑剔的特殊情况 - 理想情况下,在重构事物之后,就像我之前建议的那样; - )......:

   def randomNonrootSubtree(tree):
     # we're in trouble if the tree ONLY has a root w/no really SUB trees;-)
     assert len(tree.thedict) > 1
     while True:
       thekey = random.choice(tree.thedict.keys())
       subtree = tree.thedict[thekey]
       if subtree.parent: return thekey

最后......:

   def theSwapper(t1, t2):
     k1 = randomNonrootSubtree(t1)
     k2 = randomNonrootSubtree(t2)
     st1, p1, b1 = removeSubtreeFromTree(t1, k1)
     st2, p2, b2 = removeSubtreeFromTree(t2, k2)
     addNewSubtree(t1, st2, p1, b1)
     addNewSubtree(t2, st1, p2, b2)

答案 1 :(得分:0)

如果我理解正确,你正在寻找这样的东西......

(我没有测试过这个。)

def swap_nodes(dict_1, key_1, dict_2, key_2):
    node_1 = dict_1[key_1]
    node_2 = dict_2[key_2]

    # Update dicts and seq fields for the two nodes...
    dict_1[key_1] = node_2
    node_2.seq = key_1
    dict_2[key_2] = node_1
    node_1.seq = key_2

    # Update the parents...
    if node_1.branch == "left":
        node_1.parent.left = node_2
    else:
        node_1.parent.right = node_2

    if node_2.branch == "left":
        node_2.parent.left = node_1
    else:
        node_2.parent.right = node_1

    # Now update the branch and parent fields of the nodes...
    node_1.branch, node_2.branch = node_2.branch, node_1.branch
    node_1.parent, node_2.parent = node_2.parent, node_1.parent