0
votes

I'm not a programmer, but I'm currently experimenting with binary trees in Python, and I'm wanting to create a good method for printing out the binary tree, level by level; currently I have implemented a breadth-first method, printing each level starting at the root - which works fine, but was interested in a widely accepted recursive solution.

If I was to use a recursive method, that doesn't simply emulate the iterative breadth-first method - would I/could I use depth-first iterative deepening solution?

I know depth-first iterative deepening is used as an efficient search method, could it be used to give me a level by level print of my tree? Or is the accepted solution here to simply keep my breadth-first method?

Here is code to my tree class, the instantiation creates a tree structure of Fibonacci function recursion calls; I have amended this so that is saves the level into each Node:

from collections import deque

class FibTree(object):
"""Class which builds binary tree from Fibonacci function call graph"""
def __init__(self, n, parent, level=None):
    if level is None:
        level = 0
    self.n = n
    self.parent = parent
    self.level = level
    if n < 2:
        self.left = None
        self.right = None
        self.value = n
    else:
        self.left = FibTree(n - 1, self, level + 1)
        self.right = FibTree(n - 2, self, level + 1)
        self.value = self.left.value + self.right.value

The Iterative Breadth-First traversal method implemented:

def level_order_breadth_first_traversal(self):
    """Level order Breadth First Traversal, returning nodes by level"""
    this_level = deque([self])  
    while this_level:
        next_level = deque()
        yield this_level
        while this_level:
            u = this_level.popleft()
            if u.left is not None:
                next_level.extend([u.left])
            if u.right is not None:
                next_level.extend([u.right])
        this_level = next_level

Could I in any way now make use of the Level order stored in each Node in my Breadth-First Traversal to Print out my Binary Tree, or do I have to do some kind of traversal regardless to the fact that I have the level order stored into the Node.

EDIT: To show my Iteratve Deepening Depth level traversal, to attempt to print out my binary tree in level order:

def _getChildren(self,node):
    """Method to expand node"""
    hasLeft = node.left is not None
    hasRight = node.right is not None
    if hasLeft:
        yield node.left
    if hasRight:
        yield node.right

def dls(self, node, depth):
    """Depth Limited Search"""
    if (depth == 0):
        return node
    elif (depth > 0):
        print "(" + repr(node.i) + ")" + repr(node.value),
        children = self._getChildren(node)
        for child in children:
            self.dls(child, depth-1)
    else:
        return False

def iterative_deepening(self):
    """Iterative Deepening to print out binary tree level order"""
    depth = 0
    while True:
        result = self.dls(self, depth)
        if result is not False:
            return result
        depth += 1

When calling the iterative_deepening method just returns the root node, instead of a list of nodes in level order.

Thanks Alex

3
yes, you can use iteratively deepening DFS to print level by level.Eli Bendersky
Please supply pieces of code revealing your tree implementationOdomontois
Hi @Odomontois I've added into my question some of my code. Thanks AlexAlex2134

3 Answers

1
votes

Yes, iterative-deepening depth first search gives you the tree level-by-level, i.e. in exactly the same order as breadth-first search (if you expand the nodes in the same order, e.g. first left, then right).

Mind you, recursion is not considered very Pythonic, so a solution using an explicit stack might be preferable. Python also limits recursion depth, by default to 1000, so be careful with very deep tree structures.

1
votes

You can accomplish your task by using either traversal.

If you already have a non recursive solution, stick with it! It's better on memory. Though BFS keeps all nodes in memory so it's heavy on memory (O(branches^depth)).

With DFS, which is lighter on memory (O(branches*depth)), you can mark the level of a node in Node class itself as you traverse the nodes. You can also keep a map<level, List<Node>>, where level is an int.

If your tree so large that you don't want to go all the way, then it's a good idea to use iterative DFS.

0
votes

I have used BFS/DFS on graphs. Consider the following graph


                            v1-------------> v2-
                            |                |  -
                            |                |    -
                            |                |      -
                            |>               |>       - >
                            v4------------> v3----------> v6
                            -               |
                              -             |
                                -           |
                                  -         |>
                                    -------- > v5

In BFS, you begin by visiting the starting vertex v1. After visiting the starting vertex, you then visit the vertices adjacent to the starting verted.

Let us start with vertex v1.
Traverse v1.
Vertices(unvisited) adjacent to v1 are v2, v4.
Traverse v2, v4.
Vertices(unvisited) adjacent to v2 are v3, v6.
Traverse v3, v6.
Vertices(unvisited) adjacent to v4 are v5.
Traverse v5.
Vertices(unvisited) adjacent to v3 is nill.
Vertices(unvisited) adjacent to v6 is nill.
Vertices(unvisited) adjacent to v5 is nill. End.

Implementation using queue.

Algorithm: BFS(v)
1. Visit the starting vertex, v and insert it into the queue.
2. Repeat step 3 until the queue becomes empty.
3. Delete the front element from the queue. Visit all it's unvisited adjacent vertices and insert them into the queue.

If the graph is not connected then it's not possible to traverse the whole graph from a single vertex. 
So we need to execute the preceding algorithm repeatedly for all unvisited vertices in the graph.

1. Repeat step 2 for each vertex, v in the graph
2. If v is not visited:
    a. Call BFS(v)


Queue representation                    Vertices visited
--------------------------------------------------------
v1                                          v1
--------------------------------------------------------
v2 v4                                       v1, v2, v4
--------------------------------------------------------
v4 v3 v6                                    v1, v2, v4, v3, v6
--------------------------------------------------------
v3 v6 v5                                    v1, v2, v4, v3, v6, v5
--------------------------------------------------------
v6 v5                                       v1, v2, v4, v3, v6, v5
--------------------------------------------------------
v5                                          v1, v2, v4, v3, v6, v5
--------------------------------------------------------
empty                                       v1, v2, v4, v3, v6, v5
--------------------------------------------------------

In DFS, you begin the traversal by starting from any verterx v as the starting vertex. After traversing the starting vertex, you then traverse any one of the vertices adjacent to the starting vertex. Next, you traverse along any one of the vertices adjacent to the last visited vertex. This process is continued until you reach a vertex for which there are no unvisited adjacent vertices. At this point, you need to backtrack to the last visited vertex that has an unvisited adjacent vertex, and inititate a DFS from there.

Let us first traverse v1.
Vertices adjacent to v1 which are unvisited are v2 and v4. Let us choose v2.
Traverse v2.
Vertices adjacent to v2 which are unvisited are v3 and v6. Let us choose v3.
Traverse v3.
Vertices adjacent to v3 which are unvisited are v6 and v5. Let us choose v5.
Traverse v5.
Vertices adjacent to v5 which are unvisited are nill. So backtrack. 
Vertices adjacent to v3 which are unvisited is v6. Choose v6.
Traverse v6.
Vertices adjacent to v6 which are unvisited are nill. So backtrack. 
Vertices adjacent to v3 which are unvisited are nill. So backtrack. 
Vertices adjacent to v2 which are unvisited are nill. So backtrack. 
Vertices adjacent to v1 which are unvisited v4. Choose v4.
Traverse v4.
Vertices adjacent to v4 which are unvisited are nill. So backtrack. 
Vertices adjacent to v1 which are unvisited are nill. End. 

Implemention using stack

Algorithm: DFS(v)
1. Push the starting vertex, v into the stack
2. Repeat until the stack becomes empty.
    a. Pop a vertex from stack.
    b. Visit the popped vertex.
    c. Push all the unvisited vertices adjacent to the popped vertex into the stack.

If the graph is not connected then its not possible to traverse the whole graph from a single vertex. 
So we need to execute the preceding algorithm repeatedly for all unvisited vertices in the graph.

1. Repeat step 2 for each vertex, v in the graph
2. If v is not visited:
    a. Call DFS(v)


Stack representation                    Vertices visited
--------------------------------------------------------    
    v1
--------------------------------------------------------    
    v2                                      v1      
    v4
--------------------------------------------------------
    v3                                      v1, v2
    v6
    v4
--------------------------------------------------------
    v5                                      v1, v2, v3
    v6
    v4
--------------------------------------------------------
    v6                                      v1, v2, v3, v5
    v4
--------------------------------------------------------
    v4                                      v1, v2, v3, v5, v6
--------------------------------------------------------
    empty                                   v1, v2, v3, v5, v6, v4
--------------------------------------------------------