Binary Trees

Definition

a binary tree

class Node:
  def __init__(self, data):
    self.data = data
    self.left = None
    self.right = None
    
# create nodes
root = Node('A')
n1 = Node('B')
n2 = Node('C')
n3 = Node('D')
n4 = Node('E')

# setup children
root.left = n1
root.right = n2
n1.left = n3
n1.right = n4

( example and code src: codebyte)

  • depth of node n: number of nodes on the search path from root to n, not including n itself.
  • height of the tree: maximum depth of any node in the tree.

  • full binary tree: all nodes other than leaves has two children
  • perfect binary tree: every parent has two children
    • number of nodes in a perfect binary tree of height \(h\): \(2^{h+1}-1\)
    • number of leaves: \(2^{h}\)

Tree traversal

Binary search trees: left.data < root.data < right.data

  • inorder: allows data arranged in order. left –> root –> right (左根右)
  • preorder: root –> left –> right (根左右)
  • postorder: left –> right –> root (左右根)
  • level order: read level by level

Say we have binary tree of \(n\) nodes, height \(h\), for traversals:

  • time complexity = \(O(n)\);
  • space complexity = \(O(h)\).
    (Note: If each node has parent field, traversal can be done with space complexity \(O(1)\)).

Inorder traversal

def in_order(root, nodes):
    if root and root.left:     #inorder left subtree
        in_order(root.left, nodes)
    nodes.append(root.data)    #root
    if root and root.right:    #inorder right subtree
        in_order(root.right, nodes)
    return nodes

print in_order(root, []) # => ['D', 'B', 'E', 'A', 'C']

Preorder traversal

def pre_order(root, nodes):
    nodes.append(root.data)
    if root and root.left:
        pre_order(root.left, nodes)
    if root and root.right:
        pre_order(root.right, nodes)
    return nodes
print pre_order(root, []) # => ['A', 'B', 'D', 'E', 'C']

Postorder traversal

def post_order(root, nodes):
    if root and root.left:
        post_order(root.left, nodes)
    if root and root.right:
        post_order(root.right, nodes)
    nodes.append(root.data)  
    return nodes

print post_order(root, []) # => ['D', 'E', 'B', 'C', 'A']

Basically in the three blocks of codes, only nodes.append(root.data) changes its position.

Level order

  • output a single array
      def level_order(root, nodes):
          queue = [root]
          while queue:
              n = queue.pop(0)
              nodes.append(n.data)
              if n.left:
                  queue.append(n.left)
              if n.right:
                  queue.append(n.right)
          return nodes
        
      print level_order(root, []) # => ['A', 'B', 'C', 'D', 'E']
    

    利用一个queue, 首先将root加入queue, 之后每次从queue中pop出一个将其左右子加入queue序列,将其data加入输出。

  • output arrays level by level
      def levelOrder(self, root):
          """
          :type root: TreeNode
          :rtype: List[List[int]]
          """
            
          res = []
          self.dfs(root, 0, res)
          return res
    
      def dfs(self, root, depth, res):
          if root == None:
              return res
          if len(res) < depth+1:
              res.append([])
          res[depth].append(root.data)
          self.dfs(root.left, depth+1, res)
          self.dfs(root.right, depth+1, res)
    

    使用 DFS, 用 level记录深度, root 深度为0。 (See problem 102)

Tricks

  • Use recursive algorithm.
  • Use DFS and BFS

Problems

Easy

  • 098 Validate Binary Search Tree * (note)
  • 101 Symmetric Tree * (note)
  • 102 Binary Tree Level Order Traversal * (note)
  • 104 Maximum Depth of Binary Tree * (note)
  • 108 Convert Sorted Array to Binary Search Tree * (note)

Medium

  • 094 Binary Tree Inorder Traversal * (note)
  • 103 Binary Tree Zigzag Level Order Traversal * (note)
  • 105 Construct Binary Tree from Preorder and Inorder Traversal * (note)
  • 116 Populating Next Right Pointers in Each Node * (note)
  • 200 Number of Islands * (note)
  • 230 Kth Smallest Element in a BST * (note)
  • 285 Inorder Successor in BST * (note)