From f33153470d8e2e6a530dff210c409899047325fa Mon Sep 17 00:00:00 2001 From: wuliaozhiji Date: Sat, 5 Feb 2022 13:32:20 -0500 Subject: [PATCH] [+] Finish Prep5 --- practice/prep5.py | 284 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 284 insertions(+) create mode 100644 practice/prep5.py diff --git a/practice/prep5.py b/practice/prep5.py new file mode 100644 index 0000000..630b752 --- /dev/null +++ b/practice/prep5.py @@ -0,0 +1,284 @@ +"""CSC111 Winter 2022 Prep 5: Programming Exercises + +Instructions (READ THIS FIRST!) +=============================== + +This file contains the BinarySearchTree class you read about in this week's prep, +as well a few different methods for you to implement. Each of these methods should +be implemented recursively, and you should use the BST property to ensure that you +are only making the recursive calls that are required to implement each function--- +do not make any unnecessary calls! (The prep readings illustrate this idea in the +discussion of how __contains__ is implemented.) + +Finally, one TIP: don't forget about self._root in the recursive step! This was +the most common mistake students made with Prep 4 last week. Even when you are +recursing on self._left and/or self._right, you'll often need to do something with +self._root as well, at least in some cases. + +NOTE: the doctests access and assign to private attributes directly, which is +not good practice (although python_ta doesn't complain about it in doctests). +We'll fix this in this week when we implement a `BinarySearchTree.insert` method. + +We have marked each place you need to write code with the word "TODO". +As you complete your work in this file, delete each TODO comment. + +You may add additional doctests, but they will not be graded. You should test your work +carefully before submitting it! + +Copyright and Usage Information +=============================== + +This file is provided solely for the personal and private use of students +taking CSC111 at the University of Toronto St. George campus. All forms of +distribution of this code, whether as given or with any changes, are +expressly prohibited. For more information on copyright for CSC111 materials, +please consult our Course Syllabus. + +This file is Copyright (c) 2022 Mario Badr, David Liu, and Diane Horton. +""" +from __future__ import annotations +from typing import Any, Optional + + +class BinarySearchTree: + """Binary Search Tree class. + + Representation Invariants: + - (self._root is None) == (self._left is None) + - (self._root is None) == (self._right is None) + - (BST Property) if self._root is not None, then + all items in self._left are <= self._root, and + all items in self._right are >= self._root + + Note that duplicates of the root can appear in *either* the left or right subtrees. + """ + # Private Instance Attributes: + # - _root: + # The item stored at the root of this tree, or None if this tree is empty. + # - _left: + # The left subtree, or None if this tree is empty. + # - _right: + # The right subtree, or None if this tree is empty. + _root: Optional[Any] + _left: Optional[BinarySearchTree] + _right: Optional[BinarySearchTree] + + def __init__(self, root: Optional[Any]) -> None: + """Initialize a new BST containing only the given root value. + + If is None, initialize an empty tree. + """ + if root is None: + self._root = None + self._left = None + self._right = None + else: + self._root = root + self._left = BinarySearchTree(None) + self._right = BinarySearchTree(None) + + def is_empty(self) -> bool: + """Return whether this BST is empty. + + >>> bst = BinarySearchTree(None) + >>> bst.is_empty() + True + >>> bst = BinarySearchTree(10) + >>> bst.is_empty() + False + """ + return self._root is None + + def __contains__(self, item: Any) -> bool: + """Return whether is in this BST. + + >>> bst = BinarySearchTree(3) + >>> bst._left = BinarySearchTree(2) + >>> bst._right = BinarySearchTree(5) + >>> bst.__contains__(3) # or, 3 in bst + True + >>> bst.__contains__(5) + True + >>> bst.__contains__(2) + True + >>> bst.__contains__(4) + False + """ + if self.is_empty(): + return False + elif item == self._root: + return True + elif item < self._root: + return self._left.__contains__(item) # or, item in self._left + else: + return self._right.__contains__(item) # or, item in self._right + + def __str__(self) -> str: + """Return a string representation of this BST. + + This string uses indentation to show depth. + + We've provided this method for debugging purposes, if you choose to print a BST. + """ + return self._str_indented(0) + + def _str_indented(self, depth: int) -> str: + """Return an indented string representation of this BST. + + The indentation level is specified by the parameter. + + Preconditions: + - depth >= 0 + """ + if self.is_empty(): + return '' + else: + return ( + depth * ' ' + f'{self._root}\n' + + self._left._str_indented(depth + 1) + + self._right._str_indented(depth + 1) + ) + + ############################################################################ + # Prep exercises + ############################################################################ + def maximum(self) -> Optional[int]: + """Return the maximum number in this BST, or None if this BST is empty. + + Hint: Review the BST property to ensure you aren't making unnecessary + recursive calls. + + Preconditions: + - all items in this BST are integers + + >>> BinarySearchTree(None).maximum() is None # Empty BST + True + >>> BinarySearchTree(10).maximum() + 10 + >>> bst = BinarySearchTree(7) + >>> left = BinarySearchTree(3) + >>> left._left = BinarySearchTree(3) + >>> left._right = BinarySearchTree(5) + >>> right = BinarySearchTree(11) + >>> right._left = BinarySearchTree(9) + >>> right._right = BinarySearchTree(13) + >>> bst._left = left + >>> bst._right = right + >>> bst.maximum() + 13 + """ + if self._right is None or self._right.is_empty(): + return self._root + return self._right.maximum() + + def count(self, item: Any) -> int: + """Return the number of occurrences of in this BST. + + Hint: carefully review the BST property! + + >>> BinarySearchTree(None).count(148) # An empty BST + 0 + >>> bst = BinarySearchTree(7) + >>> left = BinarySearchTree(3) + >>> left._left = BinarySearchTree(3) + >>> left._right = BinarySearchTree(5) + >>> right = BinarySearchTree(11) + >>> right._left = BinarySearchTree(9) + >>> right._right = BinarySearchTree(13) + >>> bst._left = left + >>> bst._right = right + >>> bst.count(7) + 1 + >>> bst.count(3) + 2 + >>> bst.count(100) + 0 + """ + if self.is_empty(): + return 0 + + s = int(self._root == item) + + if self._root <= item: + s += self._right.count(item) + + if self._root >= item: + s += self._left.count(item) + + return s + + def items(self) -> list: + """Return all of the items in the BST in sorted order. + + Do not remove duplicates. + + You should *not* need to sort the list yourself: instead, use the BST + property and combine self._left.items(), self._root, and self._right.items() + in the correct order! + + >>> BinarySearchTree(None).items() # An empty BST + [] + >>> bst = BinarySearchTree(7) + >>> left = BinarySearchTree(3) + >>> left._left = BinarySearchTree(2) + >>> left._right = BinarySearchTree(5) + >>> right = BinarySearchTree(11) + >>> right._left = BinarySearchTree(9) + >>> right._right = BinarySearchTree(13) + >>> bst._left = left + >>> bst._right = right + >>> bst.items() + [2, 3, 5, 7, 9, 11, 13] + """ + if self.is_empty(): + return [] + + return self._left.items() + [self._root] + self._right.items() + + def smaller(self, item: Any) -> list: + """Return all the items in this BST less than in sorted order. + + Preconditions: + - all items in this BST can be compared with using <. + + As with BinarySearchTree.items, you should *not* need to sort the list + yourself! + + >>> bst = BinarySearchTree(7) + >>> left = BinarySearchTree(3) + >>> left._left = BinarySearchTree(2) + >>> left._right = BinarySearchTree(5) + >>> right = BinarySearchTree(11) + >>> right._left = BinarySearchTree(9) + >>> right._right = BinarySearchTree(13) + >>> bst._left = left + >>> bst._right = right + >>> bst.smaller(6) + [2, 3, 5] + >>> bst.smaller(13) + [2, 3, 5, 7, 9, 11] + """ + if self.is_empty(): + return [] + + if self._root < item: + return self._left.smaller(item) + [self._root] + self._right.smaller(item) + else: + return self._left.smaller(item) + + +if __name__ == '__main__': + import python_ta.contracts + + python_ta.contracts.check_all_contracts() + + import doctest + + doctest.testmod() + + import python_ta + + python_ta.check_all(config={ + 'max-line-length': 100, + 'disable': ['E1136'] + })