Unfortunately you don't provide us with the Tree
class, but let's assume it's something like:
class Tree(object):
def __init__(self):
self.data = None
self.nextkey = 0
self.thedict = {}
with the various attributes being updated accurately when new nodes are inserted. Now, while you talk about "the address saved in the dictionary", it's clear that the dict's value is NOT "an address" -- rather, it's a Node object (if you define a special method __repr__
in your node you may be able to see that in a clearer way; what you're seeing is the default representation, used for all Python objects whose type don't define or inherit __repr__
).
So, swapping random subtree between two different trees only requires care in updating all of the many redundant pieces of information that you're keeping (and that must ALL be in sync). By the way, it would be simpler if such updates were methods of Tree and/or Node and so usable for any of various kinds of "edit" (insertion, removal, etc), rather than buried deep in a function that performs the updates as part of a random swap -- that's good OO practice. But, that's somewhat of a side issue.
You also don't tell us exactly how the branch
attribute works, I'll assume it's a string, 'left' or 'right' as appropriate (or None if there's no parent, i.e., a root node).
To remove a subtree, you need to update: the parent node, setting to None its appropriate attribute; the root of the subtree, setting to None its parent and branch attributes; AND the Tree, removing that entry from the Tree's thedict
attribute. You will also need to remember what the parent and branch were in order to be able to insert some other subtree at that spot. Therefore...:
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
Now to ADD a new subtree to a given parent and branch in a Tree is simpler:
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
Note you can't just reuse the previous keys: there might be a "conflict" (assuming keys are unique only within a single given Tree... if you made them globally unique instead, then you could indeed reuse them).
Finally, putting these two operations and a little more together can be done. If you never need to "swap" a tree's very root, it's simpler (no special case needed to deal with a parentless subtree...) so I'm temporarily going to assume that (if you want more generality you WILL have to code the finicky special cases -- ideally after refactoring things to be methods as I previously suggested;-)...:
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
and at last...:
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)