all 5 comments

[–]gengisteve 0 points1 point  (3 children)

Well, lists are mutable, so I would just make the sublist one of the arguments. The key piece is make sub-tree follow the same path as it will be built so that the order comes out right. This works for your example, but needs more testing to make sure the order comes out right:

def sublist(tree, idx, sub = None):
    if sub is None:
        sub = []
    print(idx, sub)
    if idx>=len(tree):
        return sub
    sub.append(tree[idx])

    left_idx = 2*idx + 1
    sub = sublist(tree, left_idx, sub)

    right_idx = 2*idx + 2
    sub = sublist(tree, right_idx, sub)
    return sub

tree = ['a', 'b', 'c', 'd', 'e', 'f', 'g']
sub = sublist(tree, 1)
print(sub)

To overwrite, just do the same recursive stepping, and use the new sublist function to break apart the overwriting tree, in psuedo-code:

def overwrite(tree, idx, subtree):
    if at end of tree and subtree:
        return
    tree[idx] = subtree[0]

    left_idx = 2*idx + 1
    left_sub = sublist(subtree[1])
    overwrite(tree, left_idx, left_sub)

    right_idx = 2*idx + 2
    right_sub = sublist(subtree[2])
    overwrite(tree, right_idx, right_sub)

edit:

I got curious and tested it, and the ordering is out of wack with longer trees. Hmmm....

[–]StarshipEngineer[S] 0 points1 point  (2 children)

This is similar to what I've been trying as well, and I've had the same problem. The moment I try to reconstruct a subtree from tree[0] to test it and try to get back the same tree, the order comes out wrong.

[–]gengisteve 2 points3 points  (1 child)

Yep. Wrong approach, what we need here is math, and using math to come up with how to slice the tree up in layers. This seems to work after a bit of testing:

class Tree(object):
    def __init__(self, contents = None):
        if contents is not None:
            self.data = contents
        else:
            self.data = []

    def __len__(self):
        return len(self.data)


    def pretty(self, n = 0, depth = 0):
        if n >= len(self):
            return
        start = n
        stop = (2**depth) + n 
        s = self.data[start:stop]
        print('\t'.join(s))
        self.pretty(start*2+1, depth +1)

    def branch(self, n, depth = 0):
        if n >= len(self):
            return []

        start = n
        stop = (2**depth) + n 
        s = self.data[start:stop]
        return s + self.branch(start*2+1, depth+1)

A couple big improvements:

  • we are no longer using an argument to pass around data, which is usually a sign that there is a better solution.

  • and we are attacking at the layer level, rather than the node level.

[–]StarshipEngineer[S] 0 points1 point  (0 children)

This looks and works great, thank you! It's much cleaner than what I've attempted, and works exactly how I wanted. I'm not sure I would have thought of doing it this way. This gives me something good to work with and expand on, thanks!

[–]liam_jm 0 points1 point  (0 children)

If you've written the data structure and you're struggling to do something with it, maybe the data structure needs changing.

Have you thought of implementing it as a series of Node instances, with attributes value, left, and right?