Using extend correctly? - python

class BTNode(object):
"""A node in a binary tree."""
def __init__(self, item, left=None, right=None):
"""(BTNode, object, BTNode, BTNode) -> NoneType
Initialize this node to store item and have children left and right,
as well as depth 0.
"""
self.item = item
self.left = left
self.right = right
self.depth = 0 # the depth of this node in a tree
def __str__(self):
"""(BTNode) -> str
Return a "sideways" representation of the subtree rooted at this node,
with right subtrees above parents above left subtrees and each node on
its own line, preceded by as many TAB characters as the node's depth.
"""
# If there is a right subtree, add an extra TAB character in front of
# every node in its string representation, with an extra newline at the
# end (ready to be concatenated with self.item).
if self.right:
str_right = "\t" + str(self.right).replace("\n", "\n\t") + "\n"
else:
str_right = ""
# The string representation of self.item: if item is a string, use
# repr(item) so that quotes are included and special characters are
# treated correctly -- just like in a list; else use str(item).
if isinstance(self.item, str):
str_item = repr(self.item)
else:
str_item = str(self.item)
# If there is a left subtree, add an extra TAB character in front of
# every node in its string representation, with an extra newline at the
# beginning (ready to be concatenated with self.item).
if self.left:
str_left = "\n\t" + str(self.left).replace("\n", "\n\t")
else:
str_left = ""
# Put the right subtree above the root above the left subtree.
return str_right + str_item + str_left
def leaves_and_internals(self):
"""(BTNode) -> ([object], [object])
Return a pair of lists: (list of all the items stored in the leaves of
the subtree rooted at this node, list of all the items stored in the
internal nodes of the subtree rooted at this node).
"""
#Leaf: Doesn't have children
#Internal Node: Has children
leaf = []
internal = []
if not self.left and not self.right:
leaf.append(self.item)
if self.left is not None:
internal.append(self.item)
if self.right is not None:
self.right.leaves_and_internals()
self.left.leaves_and_internals()
elif self.right is not None:
internal.append(self.item)
self.right.leaves_and_internals()
print(leaf, internal)
return leaf, internal
The BTNode class is starter code given to us where we cannot edit. My responsibility is to write code for leaves_and_intervals by following the docstring given. My only problem currently is not knowing how to use extend to properly combine lists recursively.
My test block looks like so:
if __name__ == "__main__":
root = BTNode(2, BTNode(4, BTNode(6, BTNode(8, None, None), None), None), None)
root.leaves_and_internals()
And my output looks like so:
[8] []
[] [6]
[] [4]
[] [2]
The correct output should look like so (order should not matter):
[8] [6, 4, 2]
How do I correctly use extend so that leaf and internal don't keep resetting back to empty list every time it recurses? I know I must have a second set of lists for extend, just not sure how to implement it.

You do not need to explicitly declare empty list if you want to recursively solve this problem.
Just aggregate each sub-problem's result.
Here is my approach:
def leaves_and_internals(self):
if not self.left and not self.right:
#leaf.append(self.item)
return ([], [self.item])
if self.left is not None:
#internal.append(self.item)
if self.right is not None:
#return ([self.item], []) + self.right.leaves_and_internals()
return tuple(map(operator.add,([self.item], []), self.right.leaves_and_internals()))
#return ( [self.item], []) + self.left.leaves_and_internals()
return tuple( map(operator.add, ([self.item], []), self.left.leaves_and_internals()))
elif self.right is not None:
#internal.append(self.item)
#(leaf, internal) + self.right.leaves_and_internals()
#return ( [self.item], []) + self.right.leaves_and_internals()
return tuple(map(operator.add,([self.item], []), self.right.leaves_and_internals()))

I see that your code almost has the correct logic. Here is my implementation.
def leaves_and_internals(self):
leaves = []
internals = []
if self.left or self.right:
internals.append(self.item)
else:
leaves.append(self.item)
if self.left:
sub_leaves, sub_internals = self.left.leaves_and_internals()
leaves.extend(sub_leaves)
internals.extend(sub_internals)
if self.right:
sub_leaves, sub_internals = self.right.leaves_and_internals()
leaves.extend(sub_leaves)
internals.extend(sub_internals)
return leaves, internals

Related

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.

Python: Build Binary Tree with Pre and Inorder

I need help completing the recursive part of my function. The function is supposed to use my ListBinaryTree Class to help reconstruct a tree given its inorder and preorder traversal in string format: eg.
preorder = '1234567'
inorder = '3241657'
def build_tree(inorder, preorder):
head = preorder[0]
print(head)
head_pos = inorder.index(head)
print(head_pos)
left_in = inorder[:head_pos]
print(left_in)
right_in = inorder[(head_pos+1):]
print(right_in)
left_pre = preorder[1:-len(right_in)]
print(left_pre)
right_pre = preorder[-len(right_in):]
print(right_pre)
Which finds the important values in the preorder and inorder traversals and splits the tree up to determine which numbers are for the left side and right side of the tree.
An example of its input and output is:
build_tree('3241657', '1234567')
.
1
3
324
657
234
567
My class that I use to create the tree is as follows:
class ListBinaryTree:
"""A binary tree class with nodes as lists."""
DATA = 0 # just some constants for readability
LEFT = 1
RIGHT = 2
def __init__(self, root_value, left=None, right=None):
"""Create a binary tree with a given root value
left, right the left, right subtrees
"""
self.node = [root_value, left, right]
def create_tree(self, a_list):
return ListBinaryTree(a_list[0], a_list[1], a_list[2])
def insert_value_left(self, value):
"""Inserts value to the left of this node.
Pushes any existing left subtree down as the left child of the new node.
"""
self.node[self.LEFT] = ListBinaryTree(value, self.node[self.LEFT], None)
def insert_value_right(self, value):
"""Inserts value to the right of this node.
Pushes any existing left subtree down as the left child of the new node.
"""
self.node[self.RIGHT] = ListBinaryTree(value, None, self.node[self.RIGHT])
def insert_tree_left(self, tree):
"""Inserts new left subtree of current node"""
self.node[self.LEFT] = tree
def insert_tree_right(self, tree):
"""Inserts new left subtree of current node"""
self.node[self.RIGHT] = tree
def set_value(self, new_value):
"""Sets the value of the node."""
self.node[self.DATA] = new_value
def get_value(self):
"""Gets the value of the node."""
return self.node[self.DATA]
def get_left_subtree(self):
"""Gets the left subtree of the node."""
return self.node[self.LEFT]
def get_right_subtree(self):
"""Gets the right subtree of the node."""
return self.node[self.RIGHT]
def __str__(self):
return '['+str(self.node[self.DATA])+', '+str(self.node[self.LEFT])+', '+\
str(self.node[self.RIGHT])+']'
For the recursive part of the function I tried doing something like:
my_tree= ListBinaryTree(head)
while my_tree.get_value() != None:
left_tree = build_tree(left_in, left_pre)
right_tree = build_tree(right_in, right_pre)
my_tree.insert_value_left(left_tree)
my_tree.insert_value_right(right_tree)
print (my_tree)
But it returns an "index out of range" error.
Also for something like:
def build_tree(inorder, preorder):
head = preorder[0]
head_pos = inorder.index(head)
left_in = inorder[:head_pos]
right_in = inorder[(head_pos+1):]
left_pre = preorder[1:-len(right_in)]
right_pre = preorder[-len(right_in):]
if left_in:
left_tree = build_tree(left_in, left_pre)
else:
left_tree = None
if right_in:
right_tree = build_tree(right_in, right_pre)
else:
right_tree = None
my_tree = ListBinaryTree(head, left_tree, right_tree)
print(my_tree)
input
build_tree('3241657', '1234567')
returns
[3, None, None]
[4, None, None]
[2, None, None]
[6, None, None]
[7, None, None]
[5, None, None]
[1, None, None]
Can anyone please help me with the recursive part?
Thanks
You're making the recursive part much harder than necessary.
if left_in:
left_tree = build_tree(left_in, left_pre)
else:
left_tree = None
if right_in:
right_tree = build_tree(right_in, right_pre)
else:
right_tree = None
return ListBinaryTree(head, left_tree, right_tree)
You could perhaps simplify it even further by moving the checks for empty sequences up to the top of the function (e.g. if not inorder: return None) so it only needs to appear once.

Traverse tree and return a node instance after n traversals in python

The end goal is to copy a node from one tree to another tree. I want to visit each node in a binary tree and return a node instance after a number of traverses. I cannot seem to figure out how to return a specific node. Every time the node returned matches the id of the root node since I pass the root node to the function.
class node():
def __init__(self):
self.parent = None
self.left = None
self.right = None
def randnode(self, target):
global count
if target == count:
# print 'At target', '.'*25, target
return self
else:
if self.left is not None:
count += 1
self.left.randnode(target)
if self.right is not None:
count += 1
self.right.randnode(target)
If you're doing a DFS and counting iterations, you don't even need recursion, just a stack of places to try, and popping/pushing data.
def dfs(self,target):
count = 0
stack = [start]
while stack:
node = stack.pop()
if count == target:
return node
if node is None: # since we push left/right even if None
continue # stop pushing after we hit None node
stack.extend([self.left,self.right])
return -1 # ran out of nodes before count
Bonus points : swapping stack to a queue for BFS
Apart from that, you might want to pass the count as a parameter, like all self-respecting recursive calls, you can make this stateless ;-)
class node():
def __init__(self):
self.parent = None
self.left = None
self.right = None
def randnode(self, target,count=0):
if target == count:
# print 'At target', '.'*25, target
return self
if self.left is not None:
return self.left.randnode(target,count + 1)
if self.right is not None:
return self.right.randnode(target,count + 1)

Return a tuple containing the number of interior nodes and the number of leaves

How would I return a tuple containing the number of interior nodes and the number of leaves?
Below is what I got so far, but it's doesn't seem to be working properly.
Also, does anyone know of a good website where I can learn binary trees and recursion with problem sets for more practice?
class BTNode:
'''Node in binary tree'''
def __init__(self, value, left, right):
'''
Create new BTNode with value and possible children left and right'''
self.value, self.left, self.right = value, left, right
def count_nodes(n:'BTNode') -> (int, int):
'''
Return a tuple containing the number of interior nodes and the number of
leaves in the tree rooted at n, or (0,0) if n is None.
'''
if not n:
return (0,0)
else:
left_internal, left_leaves = count_nodes(n.left)
right_internal, right_leaves = count_nodes(n.right)
internal, leaf = (1 if n.left or n.right else 0,
1 if not n.left and not n.right else 0)
return (left_internal + right_internal + internal,
left_leaves + right_leaves + leaf)
class BTNode:
'''Node in binary tree'''
def __init__(self, value, left=None, right=None):
'''
Create new BTNode with value and possible children left and right
'''
self.value, self.left, self.right = value, left, right
def count_nodes(self):
'''
Return a tuple containing the number of interior nodes and the number of
leaves in the tree rooted at n, or (0,0) if n is None.
'''
if self.left is None and self.right is None:
# leaf
return (0, 1)
else:
# internal node
left_nodes, left_leaves = (0, 0) if self.left is None else self.left.count_nodes()
right_nodes, right_leaves = (0, 0) if self.right is None else self.right.count_nodes()
return (left_nodes + 1 + right_nodes, left_leaves + right_leaves)

How would I fix this? builtins.AttributeError: 'NoneType' object has no attribute '_helper'

I keep on getting this error, and I don't know how I'd go about correcting it.
I'm trying to implement method count_less in class BST. And I'm writing a helper method in class _BSTNode and calling the helper within count_less using indirect recursion.
Thank you.
class BST:
"""A Binary Search Tree."""
def __init__(self: 'BST', container: list =[]) -> None:
"""
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.
"""
return self.root._str("") if self.root else ""
def insert(self: 'BST', item: object) -> None:
"""
Insert item into this BST.
"""
if self.root:
self.root.insert(item)
else:
self.root = _BSTNode(item)
def count_less(self: 'BST', item: object) -> int:
"""
Return the number of items in this BST that are strictly less tham
item.
"""
return self.root._helper(self.root, item)
class _BSTNode:
"""A node in a BST."""
def __init__(self: '_BSTNode', item: object,
left: '_BSTNode' =None, right: '_BSTNode' =None) -> None:
"""
Initialize this node to store item and have children left and right.
"""
self.item, self.left, self.right = item, left, right
def _str(self: '_BSTNode', indent: 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.
"""
return ((self.right._str(indent + '\t') if self.right else '') +
indent + str(self.item) + '\n' +
(self.left._str(indent + '\t') if self.left else ''))
def insert(self: '_BSTNode', item: object) -> None:
"""
Insert item into the BST rooted at this node.
"""
if item < self.item:
if self.left:
self.left.insert(item)
else:
self.left = _BSTNode(item)
elif item > self.item:
if self.right:
self.right.insert(item)
else:
self.right = _BSTNode(item)
# else: # item == self.item
# pass # nothing to do: item is already in the tree
def _helper(self, item):
count = 0
if not self.item:
return 0
if self.item < item:
count += 1
count += count_less(self.left, item)
count += count_less(self.right, item)
return count
You didn't include a traceback, but the only reference to _helper in the code you've shown is here:
return self.root._helper(self.root, item)
The error message is telling you that self.root is None. Since you didn't show the code you ran to trigger this error, nobody can guess why you're calling it with self.root still None. But since BST.__init__() binds self.root to None, it's sure not surprising ;-)

Categories

Resources