diff --git a/GA3/ga3.pdf b/GA3/ga3.pdf index 3dddf62..183c031 100644 Binary files a/GA3/ga3.pdf and b/GA3/ga3.pdf differ diff --git a/GA3/ga3.tex b/GA3/ga3.tex index 3e78514..3f06aaa 100644 --- a/GA3/ga3.tex +++ b/GA3/ga3.tex @@ -222,6 +222,124 @@ Delete 12: \end{verbatim} } +The following printout was obtained by running the following BST implementation.with the following command: + +\begin{verbatim} +./tree.py 12 6 1 7 4 11 15 9 5 13 8 14 3 10 2 \| 6 2 12 +\end{verbatim} + +\begin{lstlisting}[caption=BST implementation, language=python] +#!/usr/bin/env python3 +# $\textbf{\color{red}vim}$: set ts=2 sw=2 et tw=80: + +import sys + +class Node: + def __init__(self, k): + self.key = k + self.left = None + self.right = None + self.parent = None + + def set_left(self, kNode): + kNode.parent = self + self.left = kNode + + def set_right(self, kNode): + kNode.parent = self + self.right = kNode + + +def search(tree, k): + if tree is None: + return None + elif tree.key == k: + return tree + elif k < tree.key: + return search(tree.left, k) + else: + return search(tree.right, k) + + +def insert(t, k): + insert_node(t, Node(k)) + + +def insert_node(t, node): + if node.key < t.key: + if t.left is None: + t.set_left(node) + else: + insert_node(t.left, node) + else: + if t.right is None: + t.set_right(node) + else: + insert_node(t.right, node) + + +def root_insert(t, k): + if t is None: + return k + if k.key > t.key: + t.set_right(root_insert(t.right, k)) + return left_rotate(t) + else: + t.set_left(root_insert(t.left, k)) + return right_rotate(t) + + +def unlink_me(node, to_link): + if node.parent == None: + tr = node + node.key = to_link.key + node.left = to_link.left + node.right = to_link.right + return node + elif node.parent.left == node: + node.parent.left = to_link + return to_link + else: + node.parent.right = to_link + return to_link + + +def delete(t, k): + to_delete = search(t, k) + if to_delete is None: + return + elif to_delete.left is None: + unlink_me(to_delete, to_delete.right) + elif to_delete.right is None: + unlink_me(to_delete, to_delete.left) + else: + if abs(to_delete.left.key - to_delete.key) < abs(to_delete.right.key - + to_delete.key): + ins = to_delete.right + new_branch = unlink_me(to_delete, to_delete.left) + insert_node(new_branch, ins) + else: + ins = to_delete.left + new_branch = unlink_me(to_delete, to_delete.right) + insert_node(new_branch, ins) + + +if __name__ == "__main__": + args = [x for x in sys.argv[1:]] + T = Node(int(args[0])) + for i in range(1, len(args)): + if args[i] == '|': + break + print_tree(T) + print("\nInsert " + str(args[i]) + ":") + insert(T, int(args[i])) + for i in range(i+1, len(args)): + print_tree(T) + print("\nDelete " + str(args[i]) + ":") + delete(T, int(args[i])) + print_tree(T) +\end{lstlisting} + \section{Exercise 2} \subsection{Point A} @@ -262,22 +380,22 @@ Insert 11: 4R 7R 12R Insert 15: - 11B - //// \ - 6R 12B - //// \ \ -1B 7B 15R - \ - 4R + 6B + //// \\\\ +1B 11R + \ / \ + 4R 7B 12B + \ + 15R Insert 9: - 11B - /////// \ - 6R 12B - //// \ \ -1B 7B 15R - \ \ - 4R 9R + 6B + //// \\\\\\\ +1B 11R + \ //// \ + 4R 7B 12B + \ \ + 9R 15R Insert 5: 6B @@ -289,82 +407,216 @@ Insert 5: 9R 15R Insert 13: - 11B - /////// \\\\\ - 6R 13B - //// \ / \ - 4B 7B 12R 15R - / \ \ -1R 5R 9R + 6B + //// \\\\\\\ + 4B 11R + / \ //// \\\\\ +1R 5R 7B 13B + \ / \ + 9R 12R 15R Insert 8: - 8B - //// \\\\ - 6R 11R - //// \ / \\\\\ - 4B 7R 9R 13B - / \ / \ -1R 5R 12R 15R + 6B + //// \\\\\\\\\\ + 4B 11R + / \ //// \\\\\ +1R 5R 8B 13B + / \ / \ + 7R 9R 12R 15R Insert 14: - 8B - //// \\\\ - 6B 11B - //// \ / \\\\\ - 4B 7R 9B 13B - / \ / \\\\\ -1R 5R 12B 15B - / - 14R + 11B + ////////// \\\\\ + 6R 13R + //// \\\\ / \\\\\ + 4B 8B 12B 15B + / \ / \ / +1R 5R 7R 9R 14R Insert 3: - 6B - //// \\\\ - 4B 8R - //// \ / \\\\ -1B 5B 7B 11B - \ / \\\\\ - 3R 9B 13B - / \\\\\ - 12B 15B - / - 14R + 11B + ////////// \\\\\ + 6B 13B + //// \\\\ / \\\\\ + 4R 8B 12B 15B + //// \ / \ / +1B 5B 7R 9R 14R + \ + 3R Insert 10: - 6B - //// \\\\ - 4B 8R - //// \ / \\\\\\\\ -1B 5B 7B 11B - \ ///// \\\\\ - 3R 9B 13B - \ / \\\\\ - 10R 12B 15B - / - 14R + 11B + ////////////// \\\\\ + 6B 13B + //// \\\\ / \\\\\ + 4R 8R 12B 15B + //// \ / \ / +1B 5B 7B 9B 14R + \ \ + 3R 10R Insert 2: - 6B - //// \\\\ - 4B 8R - //// \ / \\\\\\\\ - 2B 5B 7B 11B - / \ ///// \\\\\ -1R 3R 9B 13B - \ / \\\\\ - 10R 12B 15B - / - 14R + 11B + ////////////// \\\\\ + 6B 13B + //// \\\\ / \\\\\ + 4R 8R 12B 15B + //// \ / \ / + 2B 5B 7B 9B 14R + / \ \ +1R 3R 10R \end{verbatim}% }% % Assume every empty branch has as a child a black leaf node. +The following printout was generated by this red-black tree implementation in python with the following command: + +\begin{verbatim} +python3 redblack.py 12 6 1 7 4 11 15 9 5 13 8 14 3 10 2 +\end{verbatim} + +\begin{lstlisting}[caption=Red-black tree implementation, language=python] +#!/usr/bin/env python3 +# $\textbf{\color{red}vim}$: set ts=2 sw=2 et tw=80: + +import sys + +class Tree: + def __init__(self, root): + self.root = root + root.parent = self + + def set_root(self, root): + self.root = root + root.parent = self + + +class Node: + def __init__(self, k): + self.key = k + self.isBlack = True + self.left = None + self.right = None + self.parent = None + + def set_left(self, kNode): + if kNode is not None: + kNode.parent = self + self.left = kNode + + def set_right(self, kNode): + if kNode is not None: + kNode.parent = self + self.right = kNode + + +def is_black(node): + return node is None or node.isBlack + + +def insert(tree, node): + y = None + x = tree + # Imperatively find place to insert node + while x is not None: + y = x + if node.key < x.key: + x = x.left + else: + x = x.right + + node.parent = y + if y is None: + tree = node + elif node.key < y.key: + y.left = node + else: + y.right = node + node.isBlack = False + insert_fixup(tree, node) + + +def sibling(node): + if node.parent.left is node: + return node.parent.right + else: + return node.parent.left + + +def uncle(node): + return sibling(node.parent) + + +def right_rotate(x): + p = x.parent + t = x.left + x.set_left(t.right) + t.set_right(x) + if isinstance(p, Tree): + p.set_root(t) + elif p.left is x: + p.set_left(t) + else: + p.set_right(t) + + +def left_rotate(x): + p = x.parent + t = x.right + x.set_right(t.left) + t.set_left(x) + if isinstance(p, Tree): + p.set_root(t) + elif p.left is x: + p.set_left(t) + else: + p.set_right(t) + + +def insert_fixup(tree, node): + if isinstance(node.parent, Tree): # if root + node.isBlack = True + elif is_black(node.parent): + # no fixup needed + pass + elif not isinstance(node.parent.parent, Tree) and not is_black(uncle(node)): + node.parent.parent.isBlack = False + node.parent.isBlack = True + if sibling(node.parent) is not None: + sibling(node.parent).isBlack = True + insert_fixup(tree, node.parent.parent) + else: + if node.parent.parent.left is node.parent: + if node.parent.right is node: + left_rotate(node.parent) + node = node.left + right_rotate(node.parent.parent) + else: + if node.parent.left is node: + right_rotate(node.parent) + node = node.right + left_rotate(node.parent.parent) + node.parent.isBlack = True + if sibling(node) is not None: + sibling(node).isBlack = False + + +if __name__ == "__main__": + args = [x for x in sys.argv[1:]] + T = Tree(Node(int(args[0]))) + for i in range(1, len(args)): + print_tree(T.root) + print("\nInsert " + str(args[i]) + ":") + insert(T.root, Node(int(args[i]))) + print_tree(T.root) +\end{lstlisting} \subsection{Point B} A red-black tree of n distinct elements has an height between $\log(n)$ -and $2\log(n)$ thanks to the red-black tree invariant. The worst-case insertion -complexity is $\log(n)$ since finding the right place to insert is as complex as +and $2\log(n)$ (as professor Carzaniga said in class) thanks to the red-black +tree invariant. The worst-case insertion complexity is $\log(n)$ since +finding the right place to insert is as complex as a regular tree (i.e. logarithmic) and the ``fixup'' operation is logarithmic as well (the tree traversal is logarithmic while operations in each iteration are constant). In asymptotic terms, the uneven height of leaves in the tree does not make a difference @@ -393,7 +645,8 @@ FUNCTION JOIN-INTERVALS(X) if c == 0: X[2 * n + 1] $\gets$ start X[2 * n + 2] $\gets$ X[i][1] - start $\gets$ X[i+1][1] + if i < X.length: + start $\gets$ X[i+1][1] n $\gets$ n + 1 X.length $\gets$ 2 * n \end{lstlisting} diff --git a/redblack.py b/GA3/redblack.py similarity index 80% rename from redblack.py rename to GA3/redblack.py index 9cfbb19..519d337 100644 --- a/redblack.py +++ b/GA3/redblack.py @@ -107,14 +107,12 @@ def insert_fixup(tree, node): elif is_black(node.parent): # no fixup needed pass - elif isinstance(node.parent.parent, Tree): - node.parent.isBlack = True - elif not is_black(uncle(node)): + elif not isinstance(node.parent.parent, Tree) and not is_black(uncle(node)): node.parent.parent.isBlack = False node.parent.isBlack = True if sibling(node.parent) is not None: sibling(node.parent).isBlack = True - insert_fixup(tree, node.parent) + insert_fixup(tree, node.parent.parent) else: if node.parent.parent.left is node.parent: if node.parent.right is node: @@ -129,59 +127,6 @@ def insert_fixup(tree, node): node.parent.isBlack = True if sibling(node) is not None: sibling(node).isBlack = False - insert_fixup(tree, node.parent) - - -# Complexity (worst): Theta(n) -def search(tree, k): - if tree is None: - return None - elif tree.key == k: - return tree - elif k < tree.key: - return search(tree.left, k) - else: - return search(tree.right, k) - - -# Complexity (worst): Theta(n) -def min(t): - if t is None: - return None - while t.left is not None: - t = t.left - return t - - -# Complexity (worst): Theta(n) -def max(t): - if t is None: - return None - while t.right is not None: - t = t.right - return t - - -def successor(t): - if t.right is not None: - return min(t.right) - while t.parent is not None: - if t.parent.left == t: - return t.parent - else: - t = t.parent - return None - - -def predecessor(t): - if t.left is not None: - return max(t.left) - while t is not None: - if t.parent.right == t: - return t.parent - else: - t = t.parent - return None ############################################################################### # Code for printing trees, ignore this diff --git a/GA3/tree.py b/GA3/tree.py new file mode 100755 index 0000000..e89736e --- /dev/null +++ b/GA3/tree.py @@ -0,0 +1,179 @@ +#!/usr/bin/env python3 +# vim: set ts=2 sw=2 et tw=80: + +import sys + +class Node: + def __init__(self, k): + self.key = k + self.left = None + self.right = None + self.parent = None + + def set_left(self, kNode): + kNode.parent = self + self.left = kNode + + def set_right(self, kNode): + kNode.parent = self + self.right = kNode + + +def search(tree, k): + if tree is None: + return None + elif tree.key == k: + return tree + elif k < tree.key: + return search(tree.left, k) + else: + return search(tree.right, k) + + +def insert(t, k): + insert_node(t, Node(k)) + + +def insert_node(t, node): + if node.key < t.key: + if t.left is None: + t.set_left(node) + else: + insert_node(t.left, node) + else: + if t.right is None: + t.set_right(node) + else: + insert_node(t.right, node) + + +def root_insert(t, k): + if t is None: + return k + if k.key > t.key: + t.set_right(root_insert(t.right, k)) + return left_rotate(t) + else: + t.set_left(root_insert(t.left, k)) + return right_rotate(t) + + +def unlink_me(node, to_link): + if node.parent == None: + tr = node + node.key = to_link.key + node.left = to_link.left + node.right = to_link.right + return node + elif node.parent.left == node: + node.parent.left = to_link + return to_link + else: + node.parent.right = to_link + return to_link + + +def delete(t, k): + to_delete = search(t, k) + if to_delete is None: + return + elif to_delete.left is None: + unlink_me(to_delete, to_delete.right) + elif to_delete.right is None: + unlink_me(to_delete, to_delete.left) + else: + if abs(to_delete.left.key - to_delete.key) < abs(to_delete.right.key - + to_delete.key): + ins = to_delete.right + new_branch = unlink_me(to_delete, to_delete.left) + insert_node(new_branch, ins) + else: + ins = to_delete.left + new_branch = unlink_me(to_delete, to_delete.right) + insert_node(new_branch, ins) + + +############################################################################### +# Code for printing trees, ignore this + + +class Canvas: + def __init__(self, width): + self.line_width = width + self.canvas = [] + + def put_char(self, x, y, c): + if x < self.line_width: + pos = y * self.line_width + x + l = len(self.canvas) + if pos < l: + self.canvas[pos] = c + else: + self.canvas[l:] = [' '] * (pos - l) + self.canvas.append(c) + + def print_out(self): + i = 0 + for c in self.canvas: + sys.stdout.write(c) + i = i + 1 + if i % self.line_width == 0: + sys.stdout.write('\n') + if i % self.line_width != 0: + sys.stdout.write('\n') + + +def print_binary_r(t, x, y, canvas): + max_y = y + if t.left is not None: + x, max_y, lx, rx = print_binary_r(t.left, x, y + 2, canvas) + x = x + 1 + for i in range(rx, x): + canvas.put_char(i, y + 1, '/') + + middle_l = x + for c in str(t.key): + canvas.put_char(x, y, c) + x = x + 1 + middle_r = x + + if t.right is not None: + canvas.put_char(x, y + 1, '\\') + x = x + 1 + x0, max_y2, lx, rx = print_binary_r(t.right, x, y + 2, canvas) + if max_y2 > max_y: + max_y = max_y2 + for i in range(x, lx): + canvas.put_char(i, y + 1, '\\') + x = x0 + + return (x, max_y, middle_l, middle_r) + + +def print_tree(t): + print_w(t, 80) + + +def print_w(t, width): + canvas = Canvas(width) + print_binary_r(t, 0, 0, canvas) + canvas.print_out() + +############################################################################### + + +if __name__ == "__main__": + args = [x for x in sys.argv[1:]] + T = Node(int(args[0])) + for i in range(1, len(args)): + if args[i] == '|': + break + print_tree(T) + print("\nInsert " + str(args[i]) + ":") + insert(T, int(args[i])) + for i in range(i+1, len(args)): + print_tree(T) + print("\nDelete " + str(args[i]) + ":") + delete(T, int(args[i])) + print_tree(T) +