<jupyter_start><jupyter_text>Create a binary tree  Task 01: build a node* on a piece of paper, draw a tree.* Define a node, what are the three things you'd expect in a node?* Define class called `Node`, and define a constructor that takes no arguments, and sets the three instance variables to `None`.* Note: coding from a blank cell (or blank piece of paper) is good practice for interviews!<jupyter_code>## Define a node

## Solution
class Node(object):
    def __init__(self):
        self.value = None
        self.left = None
        self.right = None
node0 = Node()
print(f"""
value: {node0.value}
left: {node0.left}
right: {node0.right}
""")<jupyter_output>value: None
left: None
right: None<jupyter_text>Task 02: add a constructor that takes the value as a parameterCopy what you just made, and modify the constructor so that it takes in an optional value, which it assigns as the node's value.  Otherwise, it sets the node's value to `None`.<jupyter_code>## Your code here
## Solution

class Node(object):
        
    def __init__(self,value=None):
        self.value = value
        self.left = None
        self.right = None

## Check

node0 = Node()
print(f"""
value: {node0.value}
left: {node0.left}
right: {node0.right}
""")

node0 = Node("apple")
print(f"""
value: {node0.value}
left: {node0.left}
right: {node0.right}
""")<jupyter_output>value: None
left: None
right: None


value: apple
left: None
right: None<jupyter_text>Task 03: add functions to set and get the value of the nodeAdd functions `get_value` and `set_value`<jupyter_code># add set_value and get_value functions
# solution

class Node(object):
        
    def __init__(self,value=None):
        self.value = value
        self.left = None
        self.right = None

    def set_value(self,value):
        self.value = value
        
    def get_value():
        return self.value<jupyter_output><empty_output><jupyter_text>Task 04: add functions that assign a left child, or right childDefine a function `set_left_child` and a function `set_right_child`.  Each function takes in a node that it assigns as the left or right child, respectively.  Note that we can assume that this will replace any existing node if it's already assigned as a left or right child.Also, define `get_left_child` and `get_right_child` functions.<jupyter_code>## your code here
## Solution

class Node(object):
        
    def __init__(self,value = None):
        self.value = value
        self.left = None
        self.right = None
        
    def set_value(value):
        self.value = value
        
    def get_value():
        return self.value
        
    def set_left_child(self,node):
        self.left = node
        
    def set_right_child(self, node):
        self.right = node
        
    def get_left_child(self):
        return self.left
    
    def get_right_child(self):
        return self.right
    
## check

node0 = Node("apple")
node1 = Node("banana")
node2 = Node("orange")
node0.set_left_child(node1)
node0.set_right_child(node2)

print(f"""
node 0: {node0.value}
node 0 left child: {node0.left.value}
node 0 right child: {node0.right.value}
""")<jupyter_output>node 0: apple
node 0 left child: banana
node 0 right child: orange<jupyter_text>Task 05: check if left or right child existsDefine functions `has_left_child`, `has_right_child`, so that they return true if the node has left child, or right child respectively.<jupyter_code>## Solution

class Node(object):
        
    def __init__(self,value = None):
        self.value = value
        self.left = None
        self.right = None
        
    def set_value(self,value):
        self.value = value
        
    def get_value(self):
        return self.value
        
    def set_left_child(self,node):
        self.left = node
        
    def set_right_child(self, node):
        self.right = node
        
    def get_left_child(self):
        return self.left
    
    def get_right_child(self):
        return self.right


    def has_left_child(self):
        return self.left != None
    """
        #alternative solutions
        if self.left != None:
            return True
        else:
            return False
    """
    
    def has_right_child(self):
        return self.right != None
## check

node0 = Node("apple")
node1 = Node("banana")
node2 = Node("orange")

print(f"has left child? {node0.has_left_child()}")
print(f"has right child? {node0.has_right_child()}")

print("adding left and right children")
node0.set_left_child(node1)
node0.set_right_child(node2)

print(f"has left child? {node0.has_left_child()}")
print(f"has right child? {node0.has_right_child()}")<jupyter_output>has left child? False
has right child? False
adding left and right children
has left child? True
has right child? True<jupyter_text>Task 06: Create a binary treeCreate a class called `Tree` that has a "root" instance variable of type `Node`.Also define a get_root method that returns the root node.<jupyter_code># define a Tree class here
# solution
class Tree():
    def __init__(self):
        self.root = None<jupyter_output><empty_output><jupyter_text>Task 07: setting root node in constructorLet's modify the `Tree` constructor so that it takes an input that initializes the root node.  Choose between one of two options: 1) the constructor takes a `Node` object  2) the constructor takes a value, then creates a new `Node` object using that value.  Which do you think is better?<jupyter_code># choose option 1 or 2 (you can try both), and explain why you made this choice


# solution for 1
class Tree():
    def __init__(self,node=None):
        self.root = node
        
# solution 2
class Tree():
    def __init__(self,value=None):
        self.root = Node(value)<jupyter_output><empty_output><jupyter_text>DiscussionIt would be easier for others to use your Tree class if you let them pass in the value that they want to store, rather than ask them to create the Node object, because that requires that they know about the Node class and how to use it.  Task 08: add get method for root node<jupyter_code># add a get_root()
class Tree():
    def __init__(self,value=None):
        self.root = Node(value)
        
    def get_root(self):
        return self.root<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.09322033898305085, 'fraction_numerical': 0.009753757595139112, 'mean_word_length': 3.2754613807245385, 'pattern_counts': {'":': 0, '<': 29, '<?xml version=': 0, '>': 29, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '8', 'dir': 'jupyter-structured-clean-dedup', 'id': '35210', 'license': 'MIT', 'n_tokens_mistral': 1944, 'n_tokens_neox': 1899, 'n_words': 687, 'path': 'data_structure/01 Create_a_binary_tree_solution.ipynb', 'repo_name': 'chensuhui03/udacity_python_practice', 'size': '6254'}