Python Recursive Tree

Question:

I’m new to programming and I want to define a function that allows me to find an element in a non-binary tree, and keep track of all the parentals of that element on a list.
The tree is coded as a tuple, where index 0 is the parent, and index 1 is a list of its childrens. The list contains a tuple for each children that is composed the same way as before(index 0 is the parent, and index 1 is the children).

Example:

tree_data = (
    'Alan', [
        (
            'Bob', [
                ('Chris', []),
                (
                    'Debbie', [
                        ('Cindy', [])
                    ]
                )
            ]
        ),
        (
            'Eric', [
                ('Dan', []),
                (
                    'Fanny', [
                        ('George', [])
                    ]
                )
            ]
        ),
        ('Hannah', [])
    ]
)

Looking for ‘George’ would return the following: ['George', 'Eric'. 'Alan']

So far i have the following: I have managed to only append the element and the direct parent, but not any further.

Also if i add a return statement to the function, the result come as None. Would appreciate a little help.

lst = [] 
def list_parentals(tree, element):   
    if tree[0] == element: 
        lst.append(element)            
    else:
        for child in tree[1]:
            list_parentals(child, element)
            if child[0] == element:
                lst.append(tree[0])

Asked By: VV18

||

Answers:

Instead of externally keeping a list you can build it as you go.

def list_parentals(tree, element, parents):
    this, children = tree
    new_parents = [this] + parents
    if this == element:
        return new_parents
    else:
        for child in children:
            x = list_parentals(child, element, new_parents)
            # If x is not None, return it
            if x:
                return x

list_parentals(t, 'George', [])
# ['George', 'Fanny', 'Eric', 'Alan']
Answered By: Henry

For "my recursive function returns None", that is a very, very classical problem. You’ll find hundred of detailed answers about that on SO searching these words.

But in short, a recursive function must return something always, not just when it found something. Typically, on such an example, you must return a result when you found the correct leaf. But if you don’t want that result to be lost, then, you must also return something when you call that recursion that found the leaf. And when you call that recursion that called that recursion that found the leaf.

Keep in mind that what you get as a final result is the return value of the first call to list_parentals. If that first call doesn’t return anything, then, it doesn’t matter that some recursive subcalls did.

Secondly, the way to build such a path is precisely to take advantage of the recursion. Trying to create a list in a global variable when you find a matching leaf is not easy. And you may end up trying to compute that with a iterative algorithm, when the whole point of recursion is to do that for you.

Here is a working recursive method (I tried to keep it as close are your own code as possible)

tree=('Alan', [('Bob', [('Chris', []), ('Debbie', [('Cindy', [])])]), ('Eric', [('Dan', []), ('Fanny', [('George', [])])]), ('Hannah', [])])

def list_parentals(tree, element):
    if tree[0] == element:
        return [element]
    else:
        for child in tree[1]:
            sub=list_parentals(child, element)
            if sub:
                return [tree[0]] + sub
        return []

r=list_parentals(tree, 'George')
print(f"{r=}")

The logic of that is that list_parental(tree, 'George') returns the path in tree to leaf ‘George’, or [] (or None, or False, doesn’t matter) if no such leaf is found.
So (just think of it as a mathematical expression, not as code, for now), list_parental(('Alan', [('Bob', [('Chris', []), ('Debbie', [('Cindy', [])])]), ('Eric', [('Dan', []), ('Fanny', [('George', [])])]), ('Hannah', [])]), 'George') is just Alan plus `list_parental((‘Eric’, [(‘Dan’, []), (‘Fanny’, [(‘George’, [])])]), ‘George’).

“list_parental((‘Eric’, [(‘Dan’, []), (‘Fanny’, [(‘George’, [])])]), ‘George’)is justEricpluslist_parental((‘Fanny’, [(‘George’, [])]), ‘George’)`.

list_parental(('Fanny', [('George', [])]), 'George') is just Fanny plus list_parental(('George', []), 'George').

list_parental(('George', []), 'George') is just George.

To keep this consistent, all returns must be lists.

Hence my code.

list_parental(tree, leafName) is [leafName] is the root of the tree match leafName.

Else
list_parental(tree, leafName) is [tree[0]] + list_parental(child, leafName) if one of the child contains leafName, that is if list_parental(child, leafName) is not empty.

Answered By: chrslg
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.