Python:操纵子树
我是个笨蛋。我要感谢艾伦·唐尼、杰弗里·埃尔克纳和克里斯·迈尔斯 还有我所知道的“如何像计算机科学家一样思考” 我正在建立一个受遗传学启发的程序,以生成符合所提供问题的方程式 节点类如下所示:Python:操纵子树,python,data-structures,tree,Python,Data Structures,Tree,我是个笨蛋。我要感谢艾伦·唐尼、杰弗里·埃尔克纳和克里斯·迈尔斯 还有我所知道的“如何像计算机科学家一样思考” 我正在建立一个受遗传学启发的程序,以生成符合所提供问题的方程式 节点类如下所示: class Node(object): ''' ''' def __init__(self, cargo, left=None, right=None): self.cargo = cargo self.left = left se
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)
3: <__main__.Node object at 0x0167B6B0>}
我有一个Tree
类,它包含一个属性:self.data
,它是一系列链接的节点,形成一棵树,我可以遍历它生成一个等式
要执行交叉,我希望能够交换从树的两个实例中随机选择的子树
当正在构建self.data
时,它会构建一个字典,其中包含一个顺序键,将每个节点作为一个值保存。其中一条记录如下所示:
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)
3: <__main__.Node object at 0x0167B6B0>}
3:}
我想我会很聪明,只需从两个树实例中分别选择一个节点,然后交换它们各自的父节点node.left
或node.right
值。每个节点在其节点.branch
属性中记录它是左还是右
我不知道如何引用self.data(子节点)
来更改它
两个树实例都必须通过字典中保存的地址访问彼此的节点
恐怕我得复制和替换每一棵子树
如有任何意见,将不胜感激
谢谢
彼得·斯图尔特
加拿大纳奈莫如果我没弄错的话,你在找这样的东西
(我还没有测试过这个。)
不幸的是,您没有为我们提供树
类,但我们假设它类似于:
class Tree(object):
def __init__(self):
self.data = None
self.nextkey = 0
self.thedict = {}
插入新节点时,各种属性都会准确更新。现在,当您谈论“保存在字典中的地址”时,很明显dict的值不是“地址”——而是一个节点对象(如果您在节点中定义了一个特殊的方法\uuuuuu repr\uuuuuuu
,您可能可以更清楚地看到它;您看到的是默认表示,用于所有类型未定义或继承\uuuuu repr\uuuu
的Python对象)
因此,在两个不同的树之间交换随机子树只需要注意更新您保存的所有冗余信息(这些信息必须同步)。顺便说一句,如果这些更新是树和/或节点的方法,因此可用于任何类型的“编辑”(插入、删除等),那么会更简单,而不是深藏在作为随机交换的一部分执行更新的函数中——这是一个很好的OO实践。但是,这有点像一个次要问题
您也没有确切地告诉我们分支
属性是如何工作的,我将假定它是一个字符串,“left”或“right”(如果没有父节点,即根节点,则假定它是一个字符串)
要删除子树,您需要更新:父节点,将其相应属性设置为“无”;子树的根节点,将其父属性和分支属性设置为“无”;以及树,将该条目从树的属性中删除。您还需要记住父节点和分支是什么,以便能够在在该点插入其他子树。因此…:
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 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)
注意,与Alex的不同,这假设两个节点都不是其树的根。(为了处理这种可能性,可以将elif案例添加到父更新中,因此ELSE不会同时捕获“正确”和根案例。)。