How do I make a function that returns the number of nodes in a tree that have two children?
My class code is as follows:
class RefBinaryTree:
def __init__(self, data, left=None, right=None):
self.key = data
self.left = left
self.right = right
def insert_left(self, value):
self.left = RefBinaryTree(value, left=self.left)
def insert_right(self, value):
self.right = RefBinaryTree(value, right=self.right)
def get_left_subtree(self):
return self.left
def get_right_subtree(self):
return self.right
def set_value(self, new_value):
self.key = new_value
def get_value(self):
return self.key
def create_string(self, indent):
string = str(self.key) + '---+'
if self.left:
string += '\n(l)' + indent + self.left.create_string(indent + ' ')
if self.right:
string += '\n(r)' + indent + self.right.create_string(indent + ' ')
return string
def __str__(self):
return self.create_string(' ')
I'm guessing it would be best to use recursion. Any hints or helpful links would be awesome. Thanks.
It's really quite simple to count two-child nodes recursively. If you return a number with each function call (zero as the base case) you can simply add 1 every time you find a two-child node:
def findDoubleNodes(tree):
if tree == None or (tree.left == None and tree.right == None):
# base case
return 0
elif tree.left <> None and tree.right <> None:
# both have children, so add one to our total and go down one level
return findDoubleNodes(tree.left)+findDoubleNodes(tree.right) + 1
else:
# only one child, so only go down one level
return findDoubleNodes(tree.left)+findDoubleNodes(tree.right)
Inputting a RefBinaryTree returns the number of nodes with two children. An example:
x = RefBinaryTree(1)
x.insert_left(5)
x.left.insert_left(6)
x.left.insert_right(7)
x.left.right.insert_left(8)
x.left.right.insert_right(9)
x.left.right.right.insert_right(10)
The (lazily) created tree looks like this:
1
/
5
/ \
6 7
/ \
8 9
\
10
And findDoubleNodes(x) returns 2, as only two nodes (5 and 7) have two children.
Additionally, adding a left child to node 9 (x.left.right.right.insert_left(11)) has the expected result, returning 3.
This should do:
def countNodes(tree):
if tree is None:
return 0
left = tree.get_left_subtree()
rght = tree.get_right_subtree()
return (0 if left is None or rght is None else 1) \
+ countNodes(left) + countNodes(rght)
Related
I wrote this code and when I use print I see that I get the leaves. However, the final return from the function is None and not the sum of the leaves, which is supposed to be 7 in this example. I'd be happy to know whats wrong here. Thank you !
class Node:
def __init__(self, val=None):
self.left = None
self.right = None
self.val = val
def sum_leafs(tree):
if tree is None:
return 0
if tree.right and tree.left:
sum_leafs(tree.right)
sum_leafs(tree.left)
elif tree.right or tree.left:
if tree.right:
sum_leafs(tree.right)
elif tree.left:
sum_leafs(tree.left)
elif tree.right is None and tree.left is None:
return sum_leafs(tree.left) + 1
node = Node(10)
node.right = Node(2)
node.left = Node(11)
node.left.right = Node(5)
print(sum_leafs(node))
You forgot to add + when you sum the branches (left/right) and also you forgot to access val which is the most crucial thing for the whole thing to work.
Further, the logic can be simplified:
def sum_leafs(tree):
if tree is None:
return 0
if not tree.right and not tree.left:
return tree.val
return sum_leafs(tree.right) + sum_leafs(tree.left)
You are not adding the sums together or returning them. This can also be done with a method in the class:
class Node:
def __init__(self, val=None):
self.left = None
self.right = None
self.val = val
def sum(self):
s = 0
if self.left is not None:
s += self.left.sum()
if self.right is not None:
s += self.right.sum()
return self.val + s
node = Node(10)
node.right = Node(2)
node.left = Node(11)
node.left.right = Node(5)
print(node.sum())
returns:
28
You are not properly returning the calculated leaf sums. Try this:
class Node:
def __init__(self, val=None):
self.left = None
self.right = None
self.val = val
def sum_leafs(tree):
if tree is None:
return 0
elif tree.right and tree.left:
return sum_leafs(tree.right) + sum_leafs(tree.left)
elif tree.right or tree.left:
if tree.right:
return sum_leafs(tree.right)
elif tree.left:
return sum_leafs(tree.left)
elif tree.right is None and tree.left is None:
return tree.val
node = Node(10)
node.right = Node(2)
node.left = Node(11)
node.left.right = Node(5)
print(sum_leafs(node))
7
node
First I'm going to update your Node interface so that it's possible to set left and right branches when creating nodes -
class Node:
def __init__(self, val=None, left=None, right=None):
self.left = left
self.right = right
self.val = val
This allows us to create tress more ergonomically, such as -
t = Node(10, Node(11, None, Node(5)), Node(2))
traverse
Now we write a generic traverse procedure. This allows us to separate 1) the traversal of our tree from 2) the intended operation we want to perform on each tree element -
def traverse(tree):
if tree is None:
return
else:
yield tree.val
yield from traverse(tree.left)
yield from traverse(tree.right)
Now the need for sum_leafs disappears. We have decoupled traversal logic from summing logic. We can calculate the sum of leafs with a simple combination of sum and traverse -
print(sum(traverse(t)))
# 28
don't repeat yourself
Or, instead of summing the values, we could write a search function to find the first value that passes a predicate -
def search(test, tree):
for val in traverse(tree):
if test(val):
return val
print(search(lambda x: x < 10, t))
# 5
print(search(lambda x: x > 99, t))
# None
Or, we could simply collect each value into a list -
print(list(traverse(t)))
# [ 10, 11, 5, 2 ]
As you can see, removing the traversal logic from each function that depends on our tree can be a huge help.
without generators
If you don't like generators, you can write the eager version of traverse which always returns a list. The difference now is there is no way to partially traverse the tree. Note the similarities this program shares with the generator version -
def traverse(t):
if t is None:
return [] # <-- empty
else:
return \
[ t.val
, *traverse(t.left) # <-- yield from
, *traverse(t.right) # <-- yield from
]
print(traverse(t))
# [ 10, 11, 5, 2 ]
for this case, it ask me to do a indirect recursion. for "def count_less" function. which mean count the number of nodes which is less than the given value.For my code I will have attributeError. Can anyone help me to check my code?
class BST:
"""A Binary Search Tree."""
def __init__(self, container=[]):
"""(BST, list) -> NoneType
Initialize this BST by inserting the items from container (default [])
one by one, in the order given.
"""
# Initialize empty tree.
self.root = None
# Insert every item from container.
for item in container:
self.insert(item)
def __str__(self):
"""(BST) -> str
Return a "sideways" representation of the values in this BST, with
right subtrees above nodes above left subtrees and each value preceded
by a number of TAB characters equal to its depth.
"""
if self.root:
return self.root._str("")
else:
return ""
def count_less(self, item):
"""(BST, object) -> int
Return the number of items in this BST that are strictly less than
item.
"""
if self.root:
return self.root.count_less(item)
else:
return 0
I need to write the function body in this "_BSTNode" class, and call the class method in above "BST" class.
class _BSTNode:
"""A node in a BST."""
def __init__(self, item, left=None, right=None):
"""(_BSTNode, object, _BSTNode, _BSTNode) -> NoneType
Initialize this node to store item and have children left and right.
"""
self.item = item
self.left = left
self.right = right
def _str(self, indent):
"""(_BSTNode, str) -> str
Return a "sideways" representation of the values in the BST rooted at
this node, with right subtrees above nodes above left subtrees and each
value preceded by a number of TAB characters equal to its depth, plus
indent.
"""
if self.right:
right_str = self.right._str(indent + "\t")
else:
right_str = ""
if self.left:
left_str = self.left._str(indent + "\t")
else:
left_str = ""
return right_str + indent + str(self.item) + "\n" + left_str
def count_less(self: '_BSTNode', item: object) -> int:
"""
Return the number of items in the BST rooted at this node that are
strictly less than item.
"""
if not self.item:
return 0
elif item <= self.item:
if self.left:
return 1 + self.left.count_less(item)
return 0
elif self.item < item:
if self.left and self.right:
return 1 + self.left.count_less(item) + self.right.count_less(item)
elif self.left and not self.right:
return 1 + self.left.count_less(item)
elif self.right and not self.left:
return 1 + self.right.count_less(item)
else:
return 1
And this is my example I put in and error in my output.
>>> t = BST(container=[5,1,2,3,4,6,7,8,9])
>>> t.count_less(10)
You can depend on the falsy nature of None and use if statements to check to see if the child nodes exist. I'm pretty sure that the below code won't return the correct answer: what happens if self.item == item? It's possible for the children of both the left and right children to have items that are less than the item argument.
elif item < self.item:
if self.left:
return 1 + self.left.count_less(item)
return 1
elif item > self.item:
if self.right:
return 1 + self.right.count_less(item)
return 1
I was just wondering would anyone be able to help me. I am trying to do an inorder transversal of an AVL tree. But I keep getting an error that my function name 'r_in_order' is not defined. What is happening here and what am I missing? Here is the code:
class Node:
""" A node in a BST. It may have left and right subtrees """
def __init__(self, item, left = None, right = None):
self.item = item
self.left = left
self.right = right
class BST:
""" An implementation of a Binary Search Tree """
def __init__(self):
self.root = None
def recurse_add(self, ptr, item):
if ptr == None:
return Node(item)
elif item < ptr.item:
ptr.left = self.recurse_add(ptr.left, item)
elif item > ptr.item:
ptr.right = self.recurse_add(ptr.right, item)
return ptr
def add(self, item):
""" Add this item to its correct position on the tree """
self.root = self.recurse_add(self.root, item)
def r_count(self, ptr):
if ptr == None:
return 0
else:
return 1 + self.r_count(ptr.left) + self.r_count(ptr.right)
def count(self):
return self.r_count(self.root)
def r_height(self, ptr):
if ptr == None:
return 0
else:
return 1 + max(self.r_height(ptr.left), self.r_height(ptr.right))
def height(self):
return self.r_height(self.root)
def r_in_order(self, ptr):
if ptr != None:
r_in_order(ptr.left)
print(ptr.item + " ", end="")
r_in_order(ptr.right)
def in_order(self):
return self.r_in_order(self.root)
I am then testing the code with this:
import sys
from BST import BST
def main():
# Read each test case
line = sys.stdin.readline()
items = line.strip().split()
nums = [int(item) for item in items]
tree = BST()
for num in nums:
tree.add(num)
print("Print the elements of the tree in order:")
tree.in_order()
if __name__ == "__main__":
main()
r_in_order is a method of BST. It can only be called on a BST instance (or on the class with an instance as the first argument), but in the definition of r_in_order itself, you try to use it without one. So technically, it doesn't exist in the namespace you're trying to use it in.
Your function definition should be as follows:
def r_in_order(self, ptr):
if ptr != None:
self.r_in_order(ptr.left)
print(ptr.item + " ", end="")
self.r_in_order(ptr.right)
There is no general function r_in_order: you need to add self. to get a reference to the method you're already inside. There's also a syntax error lurking in the print statement. Try this:
def r_in_order(self, ptr):
if ptr != None:
self.r_in_order(ptr.left)
print(ptr.item, " ", end="")
self.r_in_order(ptr.right)
This runs, and yields the below (first line is input).
1 3 7 5 6 4 2
Print the elements of the tree in order:
1 2 3 4 5 6 7
I'm practicing creating a balanced binary search tree in python.
I already have these below, any idea on how to create a balance_bst funtion that passed a list of unique values that are
sorted in increasing order. It returns a reference to the root of a well-balanced binary search tree:
class LN:
def __init__(self,value,next=None):
self.value = value
self.next = next
def list_to_ll(l):
if l == []:
return None
front = rear = LN(l[0])
for v in l[1:]:
rear.next = LN(v)
rear = rear.next
return front
def str_ll(ll):
answer = ''
while ll != None:
answer += str(ll.value)+'->'
ll = ll.next
return answer + 'None'
# Tree Node class and helper functions (to set up problem)
class TN:
def __init__(self,value,left=None,right=None):
self.value = value
self.left = left
self.right = right
def height(atree):
if atree == None:
return -1
else:
return 1+ max(height(atree.left),height(atree.right))
def size(t):
if t == None:
return 0
else:
return 1 + size(t.left) + size(t.right)
def is_balanced(t):
if t == None:
return True
else:
return abs(size(t.left)-size(t.right)) <= 1 and is_balanced(t.left) and is_balanced(t.right)
def str_tree(atree,indent_char ='.',indent_delta=2):
def str_tree_1(indent,atree):
if atree == None:
return ''
else:
answer = ''
answer += str_tree_1(indent+indent_delta,atree.right)
answer += indent*indent_char+str(atree.value)+'\n'
answer += str_tree_1(indent+indent_delta,atree.left)
return answer
return str_tree_1(0,atree)
How do write the balance_bst?
def balance_bst(l):
Here is what I did:
def build_balanced_bst(l):
if l == None:
return None
else:
middle = len(l) // 2
return TN(l[middle],
build_balanced_bst(l[:middle]),
build_balanced_bst(l[middle + 1:]))
It gives me:
IndexError: list index out of range
How do I fix it?
I'm not going to write it for you since that's not what SO is about, but here's the general idea. Since the list is already sorted, the root should be the element in the middle of the list. Its left child will be the root of the balanced tree consisting of the elements to the left of the root in the list, and the right sub-tree will be the rest.
I'm trying to implement a recursive method to calculate the height of a binary tree. Here is the "height"-code:
def height(self):
if self.root==None:
return 0
return max(height(self.root.left), height(self.root.right))+1
When I try to call the function, I get the following error msg:
NameError: name 'height' is not defined
Does anybody see the problem?
This is a method of your class, hence you must call it from an instance (self) or the class itself. Though it won't work as you think, unless you define it as a staticmethod or change your call, e.g.
def height(self):
return 1 + max(self.left.height() if self.left is not None else 0,
self.right.height() if self.right is not None else 0)
or
#staticmethod
def height(self):
return 1 + max(self.height(self.left) if self.left is not None else 0,
self.height(self.right) if self.right is not None else 0)
Notice, that you shouldn't use == to compare with None (kudos to timgeb). And you must check whether child-nodes exist, too. And your algorithm doesn't work, so I've changed it slightly.
Example:
class Node:
def __init__(self, root=None, left=None, right=None):
self.root = root
self.left = left
self.right = right
def height(self):
return 1 + max(self.left.height() if self.left is not None else 0,
self.right.height() if self.right is not None else 0)
# Create a binary tree of height 4 using the binary-heap property
tree = [Node() for _ in range(10)]
root = tree[0]
for i in range(len(tree)):
l_child_idx, r_child_idx = (i + 1) * 2 - 1, (i + 1) * 2
root_idx = (i + 1) // 2
if root_idx:
tree[i].root = tree[root_idx]
if l_child_idx < len(tree):
tree[i].left = tree[l_child_idx]
if r_child_idx < len(tree):
tree[i].right = tree[r_child_idx]
print(root.height()) # -> 4
I am not sure of how you define your binary tree. But on a tree node you usually have only one root and multiple sons. I have the feeling that this method leads to an infinite loop. self.root.left and self.root.right are exactly my brother and me...
Here you probably have to call the method from the instances self.root.left and self.root.right with no extra argument :
def height(self):
if self.root==None:
return 0
return max(self.root.left.height(), self.root.right.height())+1