How do I find the maximum depth of a tree? - python

I have a tree data structure, defined as below:
class Tree(object):
def __init__(self, name='ROOT', children=None):
self.name = name
self.children = []
if children is not None:
for child in children:
self.add_child(child)
def __repr__(self):
return self.name
def add_child(self, node):
assert isinstance(node, Tree)
self.children.append(node)
I need to write a function to find the depth of the tree. Here is the function I wrote (takes a Tree as input, and returns an integer value as output), but it is not giving the right answer:
def depth(tree):
count = 1
if len(tree.children) > 0:
for child in tree.children:
count = count + 1
depth(child)
return count
How do I correct it?

While your depth(child) does do the recursive call, it does not do anything with the return value (the depth). You seem to be simply counting the nodes at a given level and calling that the depth (it's really the width).
What you need is something like (pseudo-code):
def maxDepth(node):
# No children means depth zero below.
if len(node.children) == 0:
return 0
# Otherwise get deepest child recursively.
deepestChild = 0
for each child in node.children:
childDepth = maxDepth(child)
if childDepth > deepestChild:
deepestChild = childDepth
# Depth of this node is one plus the deepest child.
return 1 + deepestChild

How about you just do a max of all the depth on each node recursively?
def max_depth(self, depth=0):
if not self.children:
return depth
return max(child.max_depth(depth + 1) for child in self.children)

Related

Counting nodes in a binary tree recursively

I have to count nodes in a binary tree recursively. I'm new to python and can't find any solution for my problem to finish my code.
This is what I have already tried. As you can see it is not complete, and I can't figure out where to go.
class Tree:
def __init__(self, root):
self.root = root
def add(self, subtree):
self.root.children.append(subtree)
class Node:
def __init__(self, value, children=None):
self.value = value
self.children = children if children is not None else []
def check_root(tree):
if tree.root is None:
return 0
if tree.root is not None:
return count_nodes(tree)
def count_nodes(tree):
if tree.root.children is not None:
j = 0
for i in tree.root.children:
j = 1 + count_nodes(tree)
return j
print(count_nodes(Tree(None))) # 0
print(count_nodes(Tree(Node(10)))) # 1
print(count_nodes(Tree(Node(5, [Node(6), Node(17)])))) #3
With every new step I'm getting different error. E.g. with this code I have exceeded maximum recursion depth.
Thank you for your time reading this. Any hint or help what to do next would be greatly appreciated.
I would start by passing the root node to the count_nodes function -
print(count_nodes(Tree(None)).root) # 0
print(count_nodes(Tree(Node(10))).root) # 1
print(count_nodes(Tree(Node(5, [Node(6), Node(17)]))).root) #3
or make a helper function for that.
Then the count_nodes function can simply look like this
def count_nodes(node):
return 1 + sum(count_nodes(child) for child in node.children)
EDIT: I have just noticed, you can have a None root, this means, you should also handle that:
def count_nodes(node):
if node is None:
return 0
return 1 + sum(count_nodes(child) for child in node.children)
And if you really want to handle tree or node in one function, you can make it a bit uglier:
def count_nodes(tree_or_node):
if isinstance(tree_or_node, Tree):
return count_nodes(tree_or_node.root)
if tree_or_node is None:
return 0
return 1 + sum(count_nodes(child) for child in tree_or_node.children)
and then you can call it like you originally did.
Your problem is that you're counting the same tree infinitely. Take a look at this line:
j = 1 + count_nodes(tree)
An Easy Way:
Lets assume, A is a binary tree with children or nodes which are not NULL. e.g.
3
/ \
7 5
\ \
6 9
/ \ /
1 11 4
Now in order to count number of nodes, we have a simple workaround.
Recursive Method: >>> get_count(root)
For a binary tree, the basic idea of Recursion is to traverse the tree in Post-Order. Here, if the current node is full, we increment result by 1 and add returned values of the left and right sub-trees such as:
class TestNode():
def __init__(self, data):
self.data = data
self.left = None
self.right = None
Now we move forward to get the count of full nodes in binary tree by using the method below:
def get_count(root):
if (root == None):
return 0
res = 0
if (root.left and root.right):
res += 1
res += (get_count(root.left) +
get_count(root.right))
return res
At the end, in order to run the code, we'll manage a main scope:
Here we create our binary tree A as given above:
if __name__ == '__main__':
root = TestNode(3)
root.left = TestNode(7)
root.right = TestNode(5)
root.left.right = TestNode(6)
root.left.right.left = TestNode(1)
root.left.right.right = TestNode(4)
Now at the end, inside main scope we will print count of binary tree nodes such as:
print(get_Count(root))
Here is the time complexity of this recursive function to get_count for binary tree A.
Time Complexity: O(n)

How to find max depth of n-ary tree using bfs?

This is my node definition :
class Node(object):
def __init__(self, val, children):
self.val = val
self.children = children
Now I've to find max depth in the tree. I'm using breadth-first search to mark the level of each node, then returning the max of levels.
This is my code :
def maxDepth(self, root):
"""
:type root: Node
:rtype: int
"""
if(root == None):
return 0
q = []
q.append(root)
level={root.val:1}
while(len(q)>0):
s = q.pop(0)
for c in s.children:
q.append(c)
level[c.val]=level[s.val]+1
return max(level.values())
It's working on some cases but giving the wrong answer in many cases. I don't understand where I'm missing the concept?
As suggested by #pfctgeorge, i was appending level according to node value, but there can be multiple nodes with same value as it's a tree, it'll give wrong answer in that cases.
Since you know where you went wrong, you could do something like below to achieve max depth of the tree-
Pseudocode:
q = []
q.offer(root)
level = 1
while q.isEmpty() == false:
size = q.size()
for i = 0 to size:
curr_node = q.poll()
for each_child in curr_node:
q.offer(each_child)
level = level + 1
return level

Get all nodes on a given level in a binary tree with python

i am trying to get a list of nodes (objetcs) in a python binary tree, i am looking for a recursive function implemented in the node object, so i will call function on the root node, and it will going down on childs nodes till reachs the specific level, and then will return those nodes in a list
My current aproach, i'm not sure if this is correct or the best way to implement it:
def get_level_nodes(self, nodes, level=1):
if self.level > level:
return nodes
if self.level == level:
nodes.append(self)
return nodes
for child in self.child_id:
nodes += child.get_level_nodes(node, level)
return nodes
# Getting the list
nodes_list = root_node.get_level_nodes([], 3)
There is no real need to pass a list of nodes around. Each node can just return the appropriate level-nodes of its own subtree, and leave the combining of neighbours to the parent:
def get_level_nodes(self, level=1):
if self.level > level:
return []
if self.level == level:
return [self]
# child_id seems an odd name
return [n for c in self.children for n in c.get_level_nodes(level)]
A more space-efficient implementation that does not build intermediate lists for each subtree would be a generator function:
def get_level_nodes(self, level=1):
if self.level > level:
return
if self.level == level:
yield self
else:
for c in self.children:
for n in c.get_level_nodes(level):
yield n
# or in Python3
# yield from c.get_level_nodes(level)
nodes_list = list(root_node.get_level_nodes(3))

Python: Create a Binary search Tree using a list

The objective of my code is to get each seperate word from a txt file and put it into a list and then making a binary search tree using that list to count the frequency of each word and printing each word in alphabetical order along with its frequency. Each word in the can only contain letters, numbers, -, or ' The part that I am unable to do with my beginner programming knowledge is to make the Binary Search Tree using the list I have (I am only able to insert the whole list in one Node instead of putting each word in a Node to make the tree). The code I have so far is this:
def read_words(filename):
openfile = open(filename, "r")
templist = []
letterslist = []
for lines in openfile:
for i in lines:
ii = i.lower()
letterslist.append(ii)
for p in letterslist:
if p not in ['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z',"'","-",' '] and p.isdigit() == False:
letterslist.remove(p)
wordslist = list("".join(letterslist).split())
return wordslist
class BinaryTree:
class _Node:
def __init__(self, value, left=None, right=None):
self._left = left
self._right = right
self._value = value
self._count = 1
def __init__(self):
self.root = None
def isEmpty(self):
return self.root == None
def insert(self, value) :
if self.isEmpty() :
self.root = self._Node(value)
return
parent = None
pointer = self.root
while (pointer != None) :
if value == pointer._value:
pointer._count += 1
return
elif value < pointer._value:
parent = pointer
pointer = pointer._left
else :
parent = pointer
pointer = pointer._right
if (value <= parent._value) :
parent._left = self._Node(value)
else :
parent._right = self._Node(value)
def printTree(self):
pointer = self.root
if pointer._left is not None:
pointer._left.printTree()
print(str(pointer._value) + " " + str(pointer._count))
if pointer._right is not None:
pointer._right.printTree()
def createTree(self,words):
if len(words) > 0:
for word in words:
BinaryTree().insert(word)
return BinaryTree()
else:
return None
def search(self,tree, word):
node = tree
depth = 0
count = 0
while True:
print(node.value)
depth += 1
if node.value == word:
count = node.count
break
elif word < node.value:
node = node.left
elif word > node.value:
node = node.right
return depth, count
def main():
words = read_words('sample.txt')
b = BinaryTree()
b.insert(words)
b.createTree(words)
b.printTree()
Since you're a beginner I'd advice to implement the tree methods with recursion instead of iteration since this will result to simpler implementation. While recursion might seem a bit difficult concept at first often it is the easiest approach.
Here's a draft implementation of a binary tree which uses recursion for insertion, searching and printing the tree, it should support the functionality you need.
class Node(object):
def __init__(self, value):
self.value = value
self.left = None
self.right = None
self.count = 1
def __str__(self):
return 'value: {0}, count: {1}'.format(self.value, self.count)
def insert(root, value):
if not root:
return Node(value)
elif root.value == value:
root.count += 1
elif value < root.value:
root.left = insert(root.left, value)
else:
root.right = insert(root.right, value)
return root
def create(seq):
root = None
for word in seq:
root = insert(root, word)
return root
def search(root, word, depth=1):
if not root:
return 0, 0
elif root.value == word:
return depth, root.count
elif word < root.value:
return search(root.left, word, depth + 1)
else:
return search(root.right, word, depth + 1)
def print_tree(root):
if root:
print_tree(root.left)
print root
print_tree(root.right)
src = ['foo', 'bar', 'foobar', 'bar', 'barfoo']
tree = create(src)
print_tree(tree)
for word in src:
print 'search {0}, result: {1}'.format(word, search(tree, word))
# Output
# value: bar, count: 2
# value: barfoo, count: 1
# value: foo, count: 1
# value: foobar, count: 1
# search foo, result: (1, 1)
# search bar, result: (2, 2)
# search foobar, result: (2, 1)
# search bar, result: (2, 2)
# search barfoo, result: (3, 1)
To answer your direct question, the reason why you are placing all of the words into a single node is because of the following statement inside of main():
b.insert(words)
The insert function creates a Node and sets the value of the node to the item you pass in. Instead, you need to create a node for each item in the list which is what your createTree() function does. The preceeding b.insert is not necessary.
Removing that line makes your tree become correctly formed, but reveals a fundamental problem with the design of your data structure, namely the printTree() method. This method seems designed to traverse the tree and recursively call itself on any child. In your initial version this function worked, because there the tree was mal-formed with only a single node of the whole list (and the print function simply printed that value since right and left were empty).
However with a correctly formed tree the printTree() function now tries to invoke itself on the left and right descendants. The descendants however are of type _Node, not of type BinaryTree, and there is no methodprintTree() declared for _Node objects.
You can salvage your code and solve this new error in one of two ways. First you can implement your BinaryTree.printTree() function as _Node.printTree(). You can't do a straight copy and paste, but the logic of the function won't have to change much. Or you could leave the method where it is at, but wrap each _left or _right node inside of a new BinaryTree so that they would have the necessary printTree() method. Doing this would leave the method where it is at, but you will still have to implement some kind of helper traversal method inside of _Node.
Finally, you could change all of your _Node objects to be _BinaryTree objects instead.
The semantic difference between a node and a tree is one of scope. A node should only be aware of itself, its direct children (left and right), and possibly its parent. A tree on the other hand can be aware of any of its descendents, no matter how far removed. This is accomplished by treating any child node as its own tree. Even a leaf, without any children at all can be thought of as a tree with a depth of 0. This behavior is what lets a tree work recursively. Your code is mixing the two together.

Find number of elements smaller than a given element in BST

I have been struggling with this problem for a while and I am a Python beginner when it comes to BST, so I would appreciate some help. I am dynamically adding elements from an (unsorted) array into BST. That part is fine, I know how to do that. The next step, proved to be impossible with my current skill set. As I am adding elements to the tree, I need to be able to find current rank of any element in the tree. I know there are subtleties in this problem, so I would need help to at least find the number of nodes that are below the given node in BST. For example, in this case, node 15 has nodes 10,5 and 13 below it, so the function will return 3. Here is my existing code [this is a problem from Cracking the coding interview, chapter 11]
class Node:
"""docstring for Node"""
def __init__(self, data):
self.data = data
self.left=None
self.right=None
self.numLeftChildren=0
self.numRightChildren=0
class BSTree:
def __init__(self):
self.root = None
def addNode(self, data):
return Node(data)
def insert(self, root, data):
if root == None:
return self.addNode(data)
else:
if data <= root.data:
root.numLeftChildren+=1
root.left = self.insert(root.left, data)
else:
root.numRightChildren+=1
root.right = self.insert(root.right, data)
return root
def getRankOfNumber(self,root,x):
if root==None:
return 0
else:
if x>root.data :
return self.getRankOfNumber(root.right,x)+root.numLeftChildren+1
elif root.data==x:
return root.numLeftChildren
else:
return self.getRankOfNumber(root.left,x)
BTree=BSTree()
root=BTree.addNode(20)
BTree.insert(root,25)
BTree.insert(root,15)
BTree.insert(root,10)
BTree.insert(root,5)
BTree.insert(root,13)
BTree.insert(root,23)
You can go by this approach:
1. Have 2 more fields in each node numLeftChildren and numRightChildren.
2. Initialize both of them to 0 when you create a new node.
3. At the time of insertion, you make a comparison if the newly added node's
key is less than root's key than you increment, root's numLeftChildren and
call recursion on root's left child with the new node.
4. Do Same thing if new node's key is greater than root's key.
Now, come back to your original problem, You have to find out the number of children in left subtree. Just find out that node in O(logN) time and just print the numLeftChildren
Time Complexity: O(logN)
PS: I have added another field numRightChildren which you can remove if you are always interested in knowing the number of nodes in left subtree only.
You could modify your BST to contain the number of nodes beneath each node.
Or you could iterate over a traditional BST from least to greatest, counting as you go, and stop counting when you find a node of the required value.
You could simplify the code by using just instances of the Node class, as your BSTree instance operates on the root node anyway. Another optimization could be to not represent duplicate values as separate nodes, but instead use a counter of occurrences of the key:
class Node:
def __init__(self, data):
self.data = data
self.left = None
self.right = None
self.num_left_children = 0
self.occurrences = 1
def insert(self, data):
if data < self.data:
if self.left is None:
self.left = Node(data)
else:
self.left.insert(data)
self.num_left_children += 1
elif data > self.data:
if self.right is None:
self.right = Node(data)
else:
self.right.insert(data)
else:
self.occurrences += 1
def get_rank(self, data):
if data < self.data:
return self.left.get_rank(data)
elif data > self.data:
return (self.occurrences + self.num_left_children +
self.right.get_rank(data))
else:
return self.num_left_children
Demo:
root = Node(20)
root.insert(25)
root.insert(15)
root.insert(10)
root.insert(10)
root.insert(5)
root.insert(13)
root.insert(23)
print(root.get_rank(15)) # 4
You can use the below function to find the number of the nodes in the tree (including the root).
def countNodes(self, root):
if root == None:
return 0
else
return (1 + countNodes(root.left) + countNodes(root.right));
To find the number of nodes that lie below root, subtract 1 from the value returned by the function. I think this will help get you started on the problem.
Your code will look like:
class Node:
"""docstring for Node"""
def init(self, data):
self.data = data
self.left=None
self.right=None
self.depth=0
class BSTree:
def init(self):
self.root = None
def addNode(self, data):
return Node(data)
def insert(self, root, data):
if root == None:
return self.addNode(data)
else:
if data <= root.data:
root.left = self.insert(root.left, data)
else:
root.right = self.insert(root.right, data)
return root
BTree=BSTree()
root=BTree.addNode(20)
BTree.insert(root,25)
BTree.insert(root,15)
BTree.insert(root,10)
BTree.insert(root,5)
BTree.insert(root,13)
BTree.insert(root,23)
BTree.insert(root,23)
numLeft = countNodes(root->left);
numRight = countNodes(root->right);
numChildren = numLeft + numRight;

Categories

Resources