Chapter 08
Chapter 08
In [1]:
import numpy as np
import time
import matplotlib.pyplot as plt
Exercises
R-8.1
The following questions refer to the tree of Figure 8.3.
/user/rt/courses/
• /user/rt/courses/
• cs016/
• cs252/
• homeworks/
• programs/
• projects/
• papers/
• demos/
There are 10 descendants, cs016/, grades, homeworks/, programs/, hw1, hw2, hw3, pr1, pr2, pr3
The first term n accounts for the number of nodes for comparing maximum values. And the second
summation term depends on tree structures. If tree structure is like above, the number of nodes at leaf is
proportional to n/2, and depth dp also proportional to n/2, which yields the worst case running time to
be O(n2). This is very inefficient since it needs to query all nodes in a tree. (Each node will be visited
multiple times!)
R-8.4
R-8.8
(a) What is the minimum number of external nodes for a proper binary tree with
height h? Justify your answer.
...
Therefore, the number of minimum external nodes for a proper binary tree is h+1.
(b) What is the maximum number of external nodes for a proper binary tree with
height h? Justify tour answer.
...
Therefore, the number of maximum external nodes for a proper binary tree is 2h
(c) Let T be a proper binary tree with height h and n nodes. Show that
log(n+1)−1≤h≤(n−1)/2
From the answer of (a),
The total number of nodes with minimum external node case: n≥2h+1(h≥2)
Therefore, h≤(n−1)/2
From the answer of (b), The total number of nodes with maximum external node case: n≤2h+1−1
Therefore, log2(n+1)−1≤h
(d) For which values of n and h can the above lower and upper bound on h be
attained with equality?
Answered at (c)
R-8.10
class Tree(ABC):
"""Abstract base class representing a tree structure."""
class Position(ABC):
"""An abstraction representing the location of a single element."""
@abstractmethod
def element(self):
"""Return the element stored at this Position."""
pass
@abstractmethod
def __eq__(self, other):
"""Return True if other Position represents the same
location."""
pass
@abstractmethod
def root(self):
"""Return Position representing the tree's root (or None if
empty)."""
pass
@abstractmethod
def parent(self, p):
"""Return Position representing p's parent (or None if p is
root)."""
pass
@abstractmethod
def num_children(self, p):
"""Return the number of children that Position p has."""
pass
@abstractmethod
def children(self, p):
"""Generate an iteration of Positions representing p's children."""
pass
@abstractmethod
def __len__(self):
"""Return the total number of elements in the tree."""
pass
def __iter__(self):
for p in self.positions():
yield p.element()
def is_empty(self):
"""Return True if the tree is empty."""
return len(self) == 0
def _height1(self):
return max(self.depth(p) for p in self.positions if
self.is_leaf(p))
@abstractmethod
def left(self, p):
"""Return a Position representing p's left child.
@abstractmethod
def right(self, p):
"""Return a Position representing p's right child.
@property
def num_children(self, p):
num_ch = 0
if self.left(p):
num_ch += 1
if self.right(p):
num_ch += 1
return num_ch
class _Node:
__slots__ = '_element', '_parent', '_left', '_right'
class Position(BinaryTree.Position):
"""An abstraction representing the location of a single element."""
def element(self):
return self._node._element
if p._node._parent is p._node:
raise ValueError('p is no longer valid')
return p._node
def __init__(self):
"""Create an intially empty binary tree."""
self._root = None
self._size = 0
self._positions = []
def __len__(self):
"""Return the total number of elements in the tree."""
return self._size
def root(self):
"""Return the root Position of the tree (or None if tree is
empty)."""
return self._make_position(self._root)
@property
def positions(self):
return self._positions
node = self._validate(p)
if node._right is not None: raise ValueError('Left child exists')
self._size += 1
node._left = self._Node(e, node)
pos = self._make_position(node._left)
self._positions.append(pos)
return pos
node = self._validate(p)
if node._right is not None: raise ValueError('Right child exists')
self._size += 1
node._right = self._Node(e, node)
pos = self._make_position(node._right)
self._positions.append(pos)
return pos
node = self._validate(p)
if self.num_children(p) == 2: raise ValueError('p has two
children')
child = node._left if node._left else node._right
if child is not None:
child._parent = node._parent
if node is self._root:
self._root = child
else:
parent = node._parent
if node is parent._left:
parent._left = child
else:
parent._right = child
self._positions.remove(p)
self._size -= 1
node._parent = node
return node._element
In [5]:
balanced_tree = LinkedBinaryTree()
balanced_tree._add_root("root")
Out[5]:
<__main__.LinkedBinaryTree.Position at 0x7f97cf8fee50>
In [6]:
balanced_tree.num_children(balanced_tree.root())
Out[6]:
0
In [7]:
balanced_tree._add_left(balanced_tree.root(), "left")
Out[7]:
<__main__.LinkedBinaryTree.Position at 0x7f97cf9140d0>
In [8]:
balanced_tree.num_children(balanced_tree.root())
Out[8]:
1
In [9]:
balanced_tree._add_right(balanced_tree.root(), "right")
Out[9]:
<__main__.LinkedBinaryTree.Position at 0x7f97cf9143d0>
In [10]:
balanced_tree.num_children(balanced_tree.root())
Out[10]:
2
R-8.15
The LinkedBinaryTree class provides only nonpublic versions of the update methods
discussed on page 319. Implement a simple subclass
named MutableLinkedBinaryTree that provides public wrapper functions for each of
the inheirted nonpublic update methods.
In [11]:
class MutableLinkedBinaryTree(LinkedBinaryTree):
"""
Provides public wrapper functions for each of the inherited nonpublic
update methods.
"""
C-8.32
Let T be a (not necessarily proper) binary tree with n nodes, and let D be the sum
of the depths of all the external nodes of T. Show that if T has the minimum
number of external nodes possible, then D is O(n) and if T has the maximum
number of external nodes possible, then D is O(nlog n).
If T has the minimum number of external nodes possible, then D is O(n) when a tree just composes a single
branch. However, if T has the maximum number of external nodes, the number of leaves will grow with
proportional to n/2, and the depth will grow as: log2(n). Therefore, D is O(nlogn).
C-8.34
For a tree T, let nI denote the number of its internal nodes, and let nE denote the
number of its external nodes. Show that if every internal node in T has exactly 3
children, then nE=2nI+1.
If every internal node have three children, then the number of nodes at depth d is represented as: 3d.
nI=∑i=0d−13i=1+∑i=1d−13i=1+3⋅(3d−1−1)3−1=1+3d−32=1+nE−32
Therefore, 2nI−2=nE−3, which eventually yields, nE=2nI+1.
C-8.40
class _Node:
__slots__ = '_element', '_parent', '_left', '_right'
class _Sentinel(_Node):
class Position(BinaryTree.Position):
"""An abstraction representing the location of a single element."""
def element(self):
return self._node._element
if p._node._parent is p._node:
raise ValueError('p is no longer valid')
return p._node
def __init__(self):
"""Create an intially empty binary tree."""
self._root = None
self._sentinel = None
self._size = 0
self._positions = []
def __len__(self):
"""Return the total number of elements in the tree."""
return self._size
def root(self):
"""Return the root Position of the tree (or None if tree is
empty)."""
return self._make_position(self._root)
@property
def positions(self):
return self._positions
node = self._validate(p)
if not isinstance(node._left, self._Sentinel): raise
ValueError('Left child exists')
self._size += 1
node._left = self._Node(e, node)
node._left._left = self._Sentinel(node._left)
node._left._right = self._Sentinel(node._left)
pos = self._make_position(node._left)
self._positions.append(pos)
return pos
node = self._validate(p)
if not isinstance(node._right, self._Sentinel): raise
ValueError('Right child exists')
self._size += 1
node._right = self._Node(e, node)
node._right._left = self._Sentinel(node._right)
node._right._right = self._Sentinel(node._right)
pos = self._make_position(node._right)
self._positions.append(pos)
return pos
node = self._validate(p)
if (not isinstance(node._left, self._Sentinel)) and (not
isinstance(node._right, self._Sentinel)): raise ValueError('p has two
children')
child = node._left if (not isinstance(node._left, self._Sentinel))
else node._right
if node is self._root:
self._root = child
self._sentinel._left = self._root
else:
parent = node._parent
if node is parent._left:
parent._left = child
else:
parent._right = child
self._positions.remove(p)
self._size -= 1
node._parent = node
return node._element
Now, Sentinel->"root_lef"
In [28]:
sentinel_tree._delete(sentinel_tree.root())
Out[28]:
'root'
In [29]:
sentinel_tree.root().element()
Out[29]:
'root_left'
In [30]:
sentinel_tree.left(sentinel_tree.root()).element()
Out[30]:
'SENTINEL'
In [31]:
sentinel_tree.right(sentinel_tree.root()).element()
Out[31]:
'SENTINEL'
_attach test.
In [32]:
base_tree = LinkedBinaryTreeSentinel()
base_tree._add_root("root")
Out[32]:
<__main__.LinkedBinaryTreeSentinel.Position at 0x7f97cf8e1eb0>
Left and right node of root should be sentinel at here:
In [33]:
base_tree.root().element()
Out[33]:
'root'
In [34]:
base_tree.left(base_tree.root()).element()
Out[34]:
'SENTINEL'
In [35]:
base_tree.right(base_tree.root()).element()
Out[35]:
'SENTINEL'
In [36]:
t1 = LinkedBinaryTreeSentinel()
t1._add_root("t1")
t2 = LinkedBinaryTreeSentinel()
t2._add_root("t2")
Out[36]:
<__main__.LinkedBinaryTreeSentinel.Position at 0x7f97cf87f1c0>
In [37]:
t1.root().element()
Out[37]:
't1'
In [38]:
t2.root().element()
Out[38]:
't2'
In [39]:
base_tree._attach(base_tree.root(), t1, t2)
In [40]:
base_tree.root().element()
Out[40]:
'root'
In [41]:
base_tree.left(base_tree.root()).element()
Out[41]:
't1'
In [42]:
base_tree.left(base_tree.left(base_tree.root())).element()
Out[42]:
'SENTINEL'
In [43]:
base_tree.right(base_tree.left(base_tree.root())).element()
Out[43]:
'SENTINEL'
In [44]:
base_tree.right(base_tree.root()).element()
Out[44]:
't2'
In [45]:
base_tree.left(base_tree.right(base_tree.root())).element()
Out[45]:
'SENTINEL'
In [46]:
base_tree.right(base_tree.right(base_tree.root())).element()
Out[46]:
'SENTINEL'
In [47]:
def make_balanced_tree(tree, position, terminal_depth):
if tree.depth(position) == terminal_depth:
return tree
In [ ]: