├── .gitignore ├── Contents ├── Chapter06 │ ├── ArrayQueue.py │ ├── ArrayStack.py │ └── is_matched.py ├── Chapter07 │ ├── CircularQueue.py │ ├── DoublyLinkedList.py │ ├── LinkedDeque.py │ ├── LinkedQueue.py │ └── LinkedStack.py ├── Chapter_02.ipynb ├── Chapter_03.ipynb ├── Chapter_04.ipynb ├── Chapter_05.ipynb ├── Chapter_06.ipynb ├── Chapter_07.ipynb ├── Chapter_08.ipynb └── Chapter_09.ipynb ├── Exercises ├── Chapter_01.ipynb ├── Chapter_02.ipynb ├── Chapter_02_P-2.36.py ├── Chapter_03.ipynb ├── Chapter_04.ipynb ├── Chapter_05.ipynb ├── Chapter_06.ipynb ├── Chapter_07.ipynb └── Chapter_08.ipynb ├── README.md ├── assets └── logo.png ├── images ├── Fig2.7.png ├── Fig7.1.png ├── Fig7.8.png ├── Fig7.9.png └── improper_tree.png └── requirements.txt /.gitignore: -------------------------------------------------------------------------------- 1 | venv/ 2 | .vscode/ 3 | 4 | # Custom 5 | staticfiles/ 6 | media/ 7 | 8 | # IDE 9 | .idea/ 10 | 11 | # Byte-compiled / optimized / DLL files 12 | __pycache__/ 13 | *.py[cod] 14 | *$py.class 15 | 16 | # C extensions 17 | *.so 18 | 19 | # Distribution / packaging 20 | .Python 21 | build/ 22 | develop-eggs/ 23 | dist/ 24 | downloads/ 25 | eggs/ 26 | .eggs/ 27 | lib/ 28 | lib64/ 29 | parts/ 30 | sdist/ 31 | var/ 32 | wheels/ 33 | *.egg-info/ 34 | .installed.cfg 35 | *.egg 36 | MANIFEST 37 | 38 | # PyInstaller 39 | # Usually these files are written by a python script from a template 40 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 41 | *.manifest 42 | *.spec 43 | 44 | # Installer logs 45 | pip-log.txt 46 | pip-delete-this-directory.txt 47 | 48 | # Unit test / coverage reports 49 | htmlcov/ 50 | .tox/ 51 | .coverage 52 | .coverage.* 53 | .cache 54 | nosetests.xml 55 | coverage.xml 56 | *.cover 57 | .hypothesis/ 58 | .pytest_cache/ 59 | 60 | # Translations 61 | *.mo 62 | *.pot 63 | 64 | # Django stuff: 65 | *.log 66 | migrations/ 67 | local_settings.py 68 | db.sqlite3 69 | 70 | # Flask stuff: 71 | instance/ 72 | .webassets-cache 73 | 74 | # Scrapy stuff: 75 | .scrapy 76 | 77 | # Sphinx documentation 78 | docs/_build/ 79 | 80 | # PyBuilder 81 | target/ 82 | 83 | # Jupyter Notebook 84 | .ipynb_checkpoints 85 | 86 | # pyenv 87 | .python-version 88 | 89 | # celery beat schedule file 90 | celerybeat-schedule 91 | 92 | # SageMath parsed files 93 | *.sage.py 94 | 95 | # Environments 96 | .env 97 | .venv 98 | env/ 99 | venv/ 100 | ENV/ 101 | env.bak/ 102 | venv.bak/ 103 | 104 | # Spyder project settings 105 | .spyderproject 106 | .spyproject 107 | 108 | # Rope project settings 109 | .ropeproject 110 | 111 | # mkdocs documentation 112 | /site 113 | 114 | # mypy 115 | .mypy_cache/ -------------------------------------------------------------------------------- /Contents/Chapter06/ArrayQueue.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on 6/17/2017 4 | Project: Data Structures and Algorithms 5 | """ 6 | 7 | 8 | class Empty(Exception): 9 | """Error attempting to access an element from an empty container.""" 10 | 11 | pass 12 | 13 | 14 | class ArrayQueue: 15 | """FIFO queue implementation using a Python list as underlying storage""" 16 | 17 | DEFAULT_CAPACITY = 10 # moderate capacity for all new queues 18 | 19 | def __init__(self): 20 | """Create an empty queue.""" 21 | self._data = [None] * ArrayQueue.DEFAULT_CAPACITY 22 | self._size = 0 23 | self._front = 0 24 | 25 | def __len__(self): 26 | """Return the number of elements in the queue.""" 27 | return self._size 28 | 29 | def is_empty(self): 30 | """Return True if the queue is empty.""" 31 | return self._size == 0 32 | 33 | def first(self): 34 | """Return (but do not remove) the element at the front of the queue. 35 | 36 | Raise Empty exception if the queue is empty. 37 | """ 38 | 39 | if self.is_empty(): 40 | raise Empty("Queue is empty") 41 | return self._data[self._front] 42 | 43 | def dequeue(self): 44 | """Remove and return the first element of the queue (i.e., FIFO). 45 | 46 | Raise Empty exception if the queue is empty. 47 | """ 48 | if self.is_empty(): 49 | raise Empty("Queue is empty") 50 | answer = self._data[self._front] 51 | self._data[self._front] = None # help garbage collection 52 | self._front = (self._front + 1) % len(self._data) 53 | self._size -= 1 54 | return answer 55 | 56 | def enqueue(self, e): 57 | """Add an element to the back of queue""" 58 | if self._size == len(self._data): 59 | self._resize(2 * len(self._data)) # double the array size 60 | avail = (self._front + self._size) % len(self._data) 61 | self._data[avail] = e 62 | self._size += 1 63 | 64 | def _resize(self, cap): 65 | """Resize to a new list of capacity >= len(self).""" 66 | old = self._data 67 | self._data = [None] * cap 68 | walk = self._front 69 | for k in range(self._size): 70 | self._data[k] = old[walk] 71 | walk = (1 + walk) % len(old) 72 | self._front = 0 73 | -------------------------------------------------------------------------------- /Contents/Chapter06/ArrayStack.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Thu Jun 15 09:38:13 2017 4 | 5 | @author: Jihoon_Kim 6 | """ 7 | 8 | 9 | # Stack 10 | 11 | 12 | class Empty(Exception): 13 | """Error attempting to access an element from an empty container.""" 14 | 15 | pass 16 | 17 | 18 | class ArrayStack: 19 | """LIFO Stack implementation using a Python list as underlying storage.""" 20 | 21 | def __init__(self): 22 | """Create an empty stack.""" 23 | self._data = [] 24 | 25 | def __len__(self): 26 | """Return the number of elements in the stack""" 27 | return len(self._data) 28 | 29 | def is_empty(self): 30 | """Return True if the stack is empty.""" 31 | return len(self._data) == 0 32 | 33 | def push(self, e): 34 | """Add element e to the top of the stack.""" 35 | self._data.append(e) 36 | 37 | def top(self): 38 | """Return (but do not remove) the element at the top of the stack. 39 | 40 | Raise Empty exception if the stack is empty. 41 | """ 42 | 43 | if self.is_empty(): 44 | raise Empty("Stack is empty") 45 | return self._data[-1] 46 | 47 | def pop(self): 48 | """Remove and return the element from the top of the stack (i.e.,LIFO) 49 | 50 | Raise Empty exception if the stack is empty. 51 | """ 52 | if self.is_empty(): 53 | raise Empty("Stack is empty") 54 | return self._data.pop() 55 | -------------------------------------------------------------------------------- /Contents/Chapter06/is_matched.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Thu Jun 15 11:21:46 2017 4 | 5 | @author: Jihoon_Kim 6 | """ 7 | 8 | import ArrayStack 9 | 10 | # An Algorithm for Matching Delimiters 11 | 12 | 13 | def is_matched(expr): 14 | """Return True if all delimiters are properly match; False otherwise.""" 15 | lefty = "({[" 16 | righty = ")}]" 17 | S = ArrayStack() 18 | 19 | for c in expr: 20 | if c in lefty: 21 | S.push(c) 22 | elif c in righty: 23 | if S.is_empty(): 24 | return False 25 | if righty.index(c) != lefty.index(S.pop()): 26 | return False 27 | return S.is_empty() 28 | 29 | 30 | def is_matched_html(raw): 31 | """Return True if all HTML tags are properly match; False otherwise.""" 32 | S = ArrayStack() 33 | j = raw.find("<") 34 | 35 | while j != -1: 36 | k = raw.find(">", j + 1) 37 | if k == -1: 38 | return False 39 | tag = raw[j + 1 : k] 40 | if not tag.startswith("/"): 41 | S.push(tag) 42 | else: 43 | if S.is_empty(): 44 | return False 45 | if tag[1:] != S.pop(): 46 | return False 47 | j = raw.find("<", k + 1) 48 | return S.is_empty() 49 | -------------------------------------------------------------------------------- /Contents/Chapter07/CircularQueue.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | 4 | class Empty(Exception): 5 | """Error attempting to access an element from an empty container.""" 6 | 7 | pass 8 | 9 | 10 | class CircularQueue(object): 11 | """Queue implementation using circularly linked list for storage""" 12 | 13 | # -------------------- nested _Node class -------------------- 14 | class _Node: 15 | """Lightweight, nonpublic class for storing a singly linked node.""" 16 | 17 | __slots__ = "_element", "_next" # streamline memory usage 18 | 19 | def __init__(self, element, next): # initialize node's fields 20 | self._element = element # reference to user's element 21 | self._next = next # reference to next node 22 | 23 | def __init__(self): 24 | """Create an empty queue""" 25 | self._tail = None # will represent tail of queue 26 | self._size = 0 # number of queue elements. 27 | 28 | def __len__(self): 29 | """Return the number of elements in the queue.""" 30 | return self._size 31 | 32 | def is_empty(self): 33 | """Return True if the queue is empty.""" 34 | return self._size == 0 35 | 36 | def first(self): 37 | """Return (but do not remove) the element at the fron of the queue. 38 | 39 | Raise Empty exception if the queue is empty. 40 | """ 41 | if self.is_empty(): 42 | raise Empty("Queue is empty") 43 | head = self._tail._next 44 | return head._element 45 | 46 | def dequeue(self): 47 | """Remove and return the first element of the queue (i.e., FIFO). 48 | 49 | Raise Empty exception if the queue is empty. 50 | """ 51 | if self.is_empty(): 52 | raise Empty("Queue is empty") 53 | oldhead = self._tail._next 54 | if self._size == 1: # removing only element 55 | self._tail = None # queue becomes empty 56 | else: 57 | self._tail._next = oldhead._next # bypass the old head 58 | self._size -= 1 59 | return oldhead._element 60 | 61 | def enqueue(self, e): 62 | """Add an element to the back of the queue.""" 63 | newest = self._Node(e, None) # node will be new tail node 64 | if self.is_empty(): 65 | newest._next = newest # initialize circularly 66 | else: 67 | newest._next = self._tail._next # new node points to head 68 | self._tail._next = newest # old tail points to new node 69 | self._tail = newest # new node becomes the tail. 70 | self._size += 1 71 | 72 | def rotate(self): 73 | """Rotate fron element to the back of the queue.""" 74 | if self._size > 0: 75 | self._tail = self._tail._next # old head becomes new tail. 76 | -------------------------------------------------------------------------------- /Contents/Chapter07/DoublyLinkedList.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | 4 | class Empty(Exception): 5 | """Error attempting to access an element from an empty container.""" 6 | 7 | pass 8 | 9 | 10 | class _DoublyLinkedBase(object): 11 | """A base class providing a doubly linked list representation""" 12 | 13 | class _Node(object): 14 | """Lightweight, nonpublic class for storing a doubly linked node.""" 15 | 16 | __slots__ = "_element", "_prev", "_next" # streamline memory 17 | 18 | def __init__(self, element, prev, next): # initialize node's fields 19 | self._element = element 20 | self._prev = prev 21 | self._next = next 22 | 23 | def __init__(self): 24 | """Create an empty list.""" 25 | self._header = self._Node(None, None, None) 26 | self._trailer = self._Node(None, None, None) 27 | self._header._next = self._trailer 28 | self._trailer._prev = self._header 29 | self._size = 0 30 | 31 | def __len__(self): 32 | """Return the number of elements in the list.""" 33 | return self._size 34 | 35 | def is_empty(self): 36 | """Return True if list is empty.""" 37 | return self._size == 0 38 | 39 | def _insert_between(self, e, predecessor, successor): 40 | """Ad element e between two existing nodes and return new node.""" 41 | newest = self._Node(e, predecessor, successor) 42 | predecessor._next = newest 43 | successor._prev = newest 44 | self._size += 1 45 | return newest 46 | 47 | def _delete_node(self, node): 48 | """Delete non sentinel node from teh list and return its element.""" 49 | predecessor = node._prev 50 | successor = node._next 51 | predecessor._next = successor 52 | successor._prev = predecessor 53 | self._size -= 1 54 | element = node._element 55 | node._prev = node._next = node._element = None 56 | return element 57 | -------------------------------------------------------------------------------- /Contents/Chapter07/LinkedDeque.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from Concepts.Chapter07.DoublyLinkedList import _DoublyLinkedBase 4 | 5 | 6 | class LinkedDeque(_DoublyLinkedBase): 7 | """Double-ended queue implementation based on a doubly linked list.""" 8 | 9 | def first(self): 10 | """Return (but do not remove) the element at the front of the deque.""" 11 | if self.is_empty(): 12 | raise Empty("Deque is Empty") 13 | return self._header._next._elment 14 | 15 | def last(self): 16 | """Return (but do not remove) the element at the back of the deque.""" 17 | if self.is_empty(): 18 | raise Empty("Deque is Empty") 19 | return self._trailer._prev._element 20 | 21 | def insert_first(self, e): 22 | """Add an element to the front of the deque.""" 23 | self._insert_between(e, self._header, self._header._next) 24 | 25 | def insert_last(self, e): 26 | """Insert an element to the back of the deque.""" 27 | self._insert_between(e, self._trailer._prev, self._trailer) 28 | 29 | def delete_first(self): 30 | """Remove and return the element from the front of the deque. 31 | 32 | Raise Empty exception if the deque is empty. 33 | """ 34 | if self.is_empty(): 35 | raise Empty("Deque is empty") 36 | return self._delete_node(self._header._next) 37 | 38 | def delete_last(self): 39 | """Remove and return the element from the back of the deque. 40 | 41 | Raise Empty exception if the deque is empty. 42 | """ 43 | if self.is_empty(): 44 | raise Empty("Deque is empty") 45 | return self._delete_node(self._trailer._prev) 46 | -------------------------------------------------------------------------------- /Contents/Chapter07/LinkedQueue.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | 4 | class Empty(Exception): 5 | """Error attempting to access an element from an empty container.""" 6 | 7 | pass 8 | 9 | 10 | class LinkedQueue: 11 | """FIFO queue implementation using a singly linked list for storage.""" 12 | 13 | # -------------------- nested _Node class -------------------- 14 | class _Node: 15 | """Lightweight, nonpublic class for storing a singly linked node.""" 16 | 17 | __slots__ = "_element", "_next" # streamline memory usage 18 | 19 | def __init__(self, element, next): # initialize node's fields 20 | self._element = element # reference to user's element 21 | self._next = next # reference to next node 22 | 23 | def __init__(self): 24 | """Create an empty queue.""" 25 | self._head = None 26 | self._tail = None 27 | self._size = 0 28 | 29 | def __len__(self): 30 | """Return the number of elements in the queue.""" 31 | return self._size 32 | 33 | def is_empty(self): 34 | """Return True if the queue is empty.""" 35 | return self._size == 0 36 | 37 | def first(self): 38 | """Return (but do not remove) the element at the front of the queue.""" 39 | if self.is_empty(): 40 | raise Empty("Queue is empty") 41 | return self._head._element 42 | 43 | def dequeue(self): 44 | """Remove and return the first element of the queue (i.e., FIFO). 45 | 46 | Raise Empty exception if the queue is empty. 47 | """ 48 | if self.is_empty(): 49 | raise Empty("Queue is empty") 50 | answer = self._head._element 51 | self._head = self._head._next 52 | self._size -= 1 53 | if self.is_empty(): 54 | self._tail = None 55 | return answer 56 | 57 | def enqueue(self, e): 58 | """Add an element to the back of queue""" 59 | newest = self._Node(e, None) 60 | if self.is_empty(): 61 | self._head = newest 62 | else: 63 | self._tail._next = newest 64 | self._tail = newest 65 | self._size += 1 66 | -------------------------------------------------------------------------------- /Contents/Chapter07/LinkedStack.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | 4 | class Empty(Exception): 5 | """Error attempting to access an element from an empty container.""" 6 | 7 | pass 8 | 9 | 10 | class LinkedStack(object): 11 | """LIFO Stack implementation using a singly linked list for storage.""" 12 | 13 | # -------------------- nested _Node class -------------------- 14 | class _Node: 15 | """Lightweight, nonpublic class for storing a singly linked node.""" 16 | 17 | __slots__ = "_element", "_next" # streamline memory usage 18 | 19 | def __init__(self, element, next): # initialize node's fields 20 | self._element = element # reference to user's element 21 | self._next = next # reference to next node 22 | 23 | # -------------------- stack methods -------------------- 24 | def __init__(self): 25 | """Create an empty stack.""" 26 | self._head = None 27 | self._size = 0 28 | 29 | def __len__(self): 30 | """Return the number of elements in the stack.""" 31 | return self._size 32 | 33 | def is_empty(self): 34 | """Return True if the stack is empty.""" 35 | return self._size == 0 36 | 37 | def push(self, e): 38 | """Add element e to the top of the stack""" 39 | self._head = self._Node(e, self._head) # create and link a new node 40 | self._size += 1 41 | 42 | def top(self): 43 | """Return (but do not remove) the element at the top of the stack. 44 | 45 | Raise Empty exception if the stack is empty.""" 46 | if self.is_empty(): 47 | raise Empty("Stack is empty") 48 | return self._head._element # top of stack is at head of list 49 | 50 | def pop(self): 51 | """Remove and return the element from the top of the stack (i.e., LIFO). 52 | 53 | Raise Empty exception if the stack is empty. 54 | """ 55 | if self.is_empty(): 56 | raise Empty("Stack is empty") 57 | answer = self._head._element 58 | self._head = self._head._next # bypass the former top node 59 | self._size -= 1 60 | return answer 61 | -------------------------------------------------------------------------------- /Contents/Chapter_03.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "source": [ 6 | "# Chapter 03: Algorithm Analysis\n", 7 | "\n", 8 | "* **Data structure** is a systematic way of organizing and accessing data.\n", 9 | "* **Algorithm** is a step-by-step procedure for performing some task in a finite amount of time.\n", 10 | "\n", 11 | "* Limitation of Experimental Analysis\n", 12 | " 1. Experimental running times is not an objective measure for comparing several algorithms since it is heavily rely on hardware and software environments.\n", 13 | " 2. Experiments can be done only on a limited set of test inputs. We don't know what will happen for cases not included in the experiment. \n", 14 | " 3. Experimental analysis requires actual implementation of code for measuring performance." 15 | ], 16 | "metadata": { 17 | "collapsed": false, 18 | "pycharm": {} 19 | } 20 | }, 21 | { 22 | "cell_type": "markdown", 23 | "source": [ 24 | "Therefore, our way of analyzing the efficiency of algorithms should overcome the defects above.\n", 25 | "\n", 26 | "1. Counting Primitive Operations\n", 27 | " \n", 28 | " We define a set of **primitive operations** such as following:\n", 29 | " * Assigning an identifier to an object\n", 30 | " * Determining the object associated with an identifier\n", 31 | " * Performing an arithmetic operation\n", 32 | " * Comparing two numbers\n", 33 | " * Accessing a single element of a Python `list` by index\n", 34 | " * Calling a function (excluding operations executed wihtin the function)\n", 35 | " * Returning from a function\n", 36 | " \n", 37 | " Formally, a primitive operation corresponds to a low-level instruction with an execution time that is constant. Instead of trying to determine the specific execution time of each primitive operation, we will simply count how many primitive operations are executed, and use this number, $t$, of primitive operations an algorithm performs will be proportional to the actual running time of that algorihtm.\n", 38 | " \n", 39 | "2. Measuring Operations as a Function of Input Size\n", 40 | "\n", 41 | " To capture the order of growth of an algorithm's running time, we will associate, with each algorithm, a function $f(n)$ that characterizes the number of primitive operations that are performed as a function of the input isze $n$.\n", 42 | " \n", 43 | "3. Focusing on the Worst-Case Input\n", 44 | "\n", 45 | " An algorithm may run faster on some inputs than it does on others of the same size. Thus, we may wish to express the running time of an algorithm as the function of the input size obtained by taking the average over all possible inputs of the same size. Unfortunately, such an **average-case** analysis is typically quite challenging. It requires us to define a probability distribution on the set of inputs, which is often a difficult task.\n", 46 | " \n", 47 | " An average-case analysis usually requires that we calculate expected running times based on a given input distribution, which usually involves sophisticated probability theory. Therefore, we will characterize runnning times in terms of the **worst case**, as a function of the input size, $n$, of the algorithm.\n", 48 | " \n", 49 | " Worst-case analysis si much easier than average-case analysis, as it requires only the ability to identify the wors-case input, which is often simple." 50 | ], 51 | "metadata": { 52 | "collapsed": false, 53 | "pycharm": { 54 | "name": "#%% md\n" 55 | } 56 | } 57 | }, 58 | { 59 | "cell_type": "markdown", 60 | "source": [ 61 | "## 3.2 The Seven Functions Used in This Book\n", 62 | "\n", 63 | "1. The Constant Function\n", 64 | " \n", 65 | " $$f(n) = c$$\n", 66 | "\n", 67 | "2. The Logarithm Function\n", 68 | "\n", 69 | " $$f(n) = \\log_b n$$\n", 70 | " \n", 71 | "3. The Linear Function\n", 72 | "\n", 73 | " $$f(n) = n$$\n", 74 | "\n", 75 | "4. The N-Log-N Function\n", 76 | "\n", 77 | " $$f(n) = n \\log n$$\n", 78 | "\n", 79 | "5. The Quadratic Function\n", 80 | "\n", 81 | " $$f(n) = n^2$$\n", 82 | "\n", 83 | "6. The Cubic Function and Other Polynomials\n", 84 | "\n", 85 | " $$f(n) = n^3$$\n", 86 | " \n", 87 | " $$f(n) = a_0 + a_1n + a_2n^2 + a_3n^3 + \\cdots + a_dn^d$$\n", 88 | " \n", 89 | "7. The Exponential Function\n", 90 | "\n", 91 | " $$f(n) = b^n$$\n", 92 | " " 93 | ], 94 | "metadata": { 95 | "collapsed": false, 96 | "pycharm": { 97 | "name": "#%% md\n" 98 | } 99 | } 100 | }, 101 | { 102 | "cell_type": "markdown", 103 | "source": [ 104 | "## 3.3 Asymptotic Analysis" 105 | ], 106 | "metadata": { 107 | "collapsed": false, 108 | "pycharm": { 109 | "name": "#%% md\n" 110 | } 111 | } 112 | }, 113 | { 114 | "cell_type": "markdown", 115 | "source": [ 116 | "### 3.3.1 The \"Big-O\" Notation\n", 117 | "Let $f(n)$ and $g(n)$ be the functions mapping positive integers to positive real umbers. We say that $f(n)$ is $O(g(n))$ if there is a real constant $c > 0$ and an integer constant $n_0 \\geq 1$ such that\n", 118 | "\n", 119 | "$$f(n) \\leq cg(n), \\quad \\text{for} \\quad n \\geq n_0$$\n", 120 | "\n", 121 | "This definition is often referred to as the \"big-O\" notation, for it is sometimes pronounced as $``f(n) \\ is \\ \\boldsymbol{big-O} \\ of \\ g(n).\"$\n", 122 | "\n", 123 | "The big-O notation allows us to say that a function $f(n)$ is \"less than or equal to\" another function $g(n)$ up to \n", 124 | "a constant factor and in the **asymptotic** sense as $n$ grows toward infinity." 125 | ], 126 | "metadata": { 127 | "collapsed": false, 128 | "pycharm": { 129 | "name": "#%% md\n" 130 | } 131 | } 132 | }, 133 | { 134 | "cell_type": "markdown", 135 | "source": [ 136 | "### 3.3.2 Comperative Analysis\n", 137 | "Supposee two algorithms solving the same problem are available: an algorithm $A$, which has a running time of $O(n)$, \n", 138 | "and an algorithm $B$, which has a running time of $O(n^2)$. Which algorithm is better? We know that $n$ is $O(n^2)$, which implies that algorithm $A$ is \n", 139 | "**asymptotically better** than algorithm $B$. \n", 140 | "\n", 141 | "#### Some Words of Caution\n", 142 | "A few words of caution about asymptotic anotation are in order at this point. First, note that the use of the big-O and raelated notations can e somewhat misleading should the constant factors they \"hide\" be very large.\n", 143 | "For example, while it is true that the function $10^{100}n$ is $O(n)$. if this is the running time of an algorithm being compared to one whose running time is $10n\\log n$, we should prefer the $O(n\\log n)$ time algorithm, even though the linear-time algorithm is asymptotically faster.\n", 144 | "\n", 145 | "### 3.3.3 Examples of Algorithm Analysis\n", 146 | "\n", 147 | "#### Prefix Averages\n", 148 | "Given a sequence $S$ consisting of $n$ numbers, we want to compute a sequence $A$ such that $A[j]$ is the average of elements $S[0], \\ldots, S[j]$, for $j=0,\\ldots,n-1$, that is,\n", 149 | "\n", 150 | "$$A[j] = \\frac{\\sum_{i=0}^j s[i]}{j + 1}$$\n" 151 | ], 152 | "metadata": { 153 | "pycharm": { 154 | "metadata": false, 155 | "name": "#%% md\n" 156 | } 157 | } 158 | }, 159 | { 160 | "cell_type": "markdown", 161 | "source": "##### A Quadratic-Time Algorithm\n\n```python\ndef prefix_quadratic(S):\n\n n = len(S) # O(1)\n A = [0] * n # O(n)\n for j in range(n): # O(n)\n total = 0\n for i in range(j + 1): # O(n^2)\n total += S[i] # O(n^2)\n A[j] = total / (j+1) # O(n)\n return A\n```\n\nTherefore, the running time of `prefix_quadratic` is $O(n^2)$\n", 162 | "metadata": { 163 | "pycharm": { 164 | "metadata": false, 165 | "name": "#%% md\n" 166 | } 167 | } 168 | }, 169 | { 170 | "cell_type": "markdown", 171 | "source": "\n```python\ndef prefix_linear(S):\n\n n = len(S) # O(1)\n A = [0] * n # O(n)\n total = 0\n for j in range(n): # O(n)\n total += S[j] # O(n)\n A[j] = total / (j+1) # O(n)\n return A\n```\n\nTherefore, the running time of `prefix_linear` is $O(n)$.\n", 172 | "metadata": { 173 | "pycharm": { 174 | "metadata": false, 175 | "name": "#%% md\n" 176 | } 177 | } 178 | }, 179 | { 180 | "cell_type": "markdown", 181 | "source": "#### Three-Way Set Disjointness\nSuppose we are given three sequences of numbers, $A, B$ and $C$. We will assume that no individual sequence contains duplicate values, but that there may be some numbers that are in two or three of the sequences.\nThe **three-way set disjointness** problem is to determine if the intersection of the three sequences is empty, namely, that there is no element $x$ such that $x \\in A, x \\in B$ and $x \\in C$.\n\n```python\ndef disjoint1(A, B, D):\n for a in A:\n for b in B:\n for c in C:\n if a == b == c:\n return False\n return True\n```\n\nIf each of the original sets has size $n$, then the worst-case running time of this function is $O(n^3)$.\n\n```python\ndef disjoint2(A, B, D):\n for a in A:\n for b in B:\n if a== b:\n for c in C:\n if a == c:\n return False\n return True\n```\n\nIn the improved version, it is not simply that we save time if we get lucky. We claim that the *worst-case* running time for `disjoint2` is $O(n^2)$, since the innermost loop, over $C$, executes at most $n$ times.\n", 182 | "metadata": { 183 | "pycharm": { 184 | "metadata": false, 185 | "name": "#%% md\n" 186 | } 187 | } 188 | }, 189 | { 190 | "cell_type": "markdown", 191 | "source": "#### Element Uniqueness\nA problem that is closely related to the three-way set disjointness problem is the **element uniqueness problem**. In the former, we are given three collections and we presumed that there were no duplicates within a single collection. IN the element uniqueness problem, we are given a single sequence $S$ with $n$ elements and asked whether all elements of that collection are distinct from each other.\n\n```python\ndef unique1(S):\n for j in range(len(S)):\n for k in range(j+1, len(S)):\n if S[j] == S[k]:\n return False\n return True\n```\n\nThe approach is $O(n^2)$.\n", 192 | "metadata": { 193 | "pycharm": { 194 | "metadata": false, 195 | "name": "#%% md\n" 196 | } 197 | } 198 | }, 199 | { 200 | "cell_type": "markdown", 201 | "source": "#### Using Sorting as a Problem-Solving Tool\n\n```python\ndef unique2(S):\n temp = sorted(S)\n for j in range(1, len(temp)):\n if S[j-1] == S[j]:\n return False\n return True\n```\n\nIt guarantees a worst-case running time of $O(n \\log n)$.", 202 | "metadata": { 203 | "pycharm": { 204 | "metadata": false, 205 | "name": "#%% md\n" 206 | } 207 | } 208 | } 209 | ], 210 | "metadata": { 211 | "language_info": { 212 | "codemirror_mode": { 213 | "name": "ipython", 214 | "version": 2 215 | }, 216 | "file_extension": ".py", 217 | "mimetype": "text/x-python", 218 | "name": "python", 219 | "nbconvert_exporter": "python", 220 | "pygments_lexer": "ipython2", 221 | "version": "2.7.6" 222 | }, 223 | "kernelspec": { 224 | "name": "python3", 225 | "language": "python", 226 | "display_name": "Python 3" 227 | }, 228 | "pycharm": { 229 | "stem_cell": { 230 | "cell_type": "raw", 231 | "source": [], 232 | "metadata": { 233 | "collapsed": false 234 | } 235 | } 236 | } 237 | }, 238 | "nbformat": 4, 239 | "nbformat_minor": 0 240 | } -------------------------------------------------------------------------------- /Contents/Chapter_04.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "source": [ 6 | "# Chapter 04: Recursion" 7 | ], 8 | "metadata": { 9 | "pycharm": { 10 | "metadata": false, 11 | "name": "#%% md\n" 12 | } 13 | } 14 | }, 15 | { 16 | "cell_type": "markdown", 17 | "source": [ 18 | "## 4.1: Illustrative Examples\n", 19 | "\n", 20 | "#### Binary Search\n", 21 | "This section covers a recursive algorithm, binary search. When the sequence is unsorted, the standard approach to search\n", 22 | "for a target value is to use a loop to examine every element, until either finding the target or exhausting the \n", 23 | "data set. This is known as the sequential search algorithm. This algorithm runs in $O(n)$ time since every element is \n", 24 | "inspected in the worst case.\n", 25 | "\n", 26 | "When the sequence is sorted and indexable, there is a much more efficient algorithm. For any index $j$, we know that all\n", 27 | " the values sorted at indices $0, \\ldots, j-1$ are less than or equal to the value at index $j$, and all the values\n", 28 | " sorted at indices $j+1, \\ldots, n-1$ are greater than or equal to that at index $j$.\n", 29 | " \n", 30 | " The algorithm maintains two parameters, `low` and `high` such that all the candidate entries have index at least\n", 31 | " `low` and at most `high`. Initially, `low = 0` and `high = n-1`. We then compare the target value to the median candidate,\n", 32 | " that is, the item `data[mid]` with index\n", 33 | " \n", 34 | " $$\\text{mid} = \\left\\lfloor \\frac{( \\text{low} + \\text{high})}{2} \\right\\rfloor$$\n", 35 | "\n", 36 | " \n", 37 | "Simply we have to care these three case:\n", 38 | "* If the target equals `data[mid]`, then we have found the item we are looking for, and the search terminates successfully.\n", 39 | "* If `target < data[mid]`, then we recur on the first half of the sequence, indices from $(0, \\text{mid} -1)$.\n", 40 | "* If `target > data[mid]`, then we recur on the second half of the sequence, indices from $(\\text{mid} +1, \\text{high})$\n", 41 | "\n", 42 | "When `low > high`, means the sequence does not have the value we are finding.\n" 43 | ], 44 | "metadata": { 45 | "pycharm": { 46 | "metadata": false, 47 | "name": "#%% md\n", 48 | "is_executing": false 49 | } 50 | } 51 | }, 52 | { 53 | "cell_type": "code", 54 | "execution_count": 1, 55 | "outputs": [], 56 | "source": [ 57 | "def binary_search(data, target, low, high):\n", 58 | " \"\"\"\n", 59 | " Return True if target is found in indicated portion of a Python list\n", 60 | " \n", 61 | " The search only considers the portion from data[low] to data[high] inclusive.\n", 62 | " \n", 63 | " :param data: sequence that value will be searched\n", 64 | " :param target: value to find\n", 65 | " :param low: lower bound of sequence's index\n", 66 | " :param high: upper bound of sequence's index\n", 67 | " :return: \n", 68 | " \"\"\"\n", 69 | " \n", 70 | " if low > high:\n", 71 | " return False\n", 72 | " else:\n", 73 | " mid = (low + high) // 2\n", 74 | " if target == data[mid]:\n", 75 | " return True\n", 76 | " elif target < data[mid]:\n", 77 | " return binary_search(data, target, low, mid -1)\n", 78 | " else:\n", 79 | " return binary_search(data, target, mid+1, high)" 80 | ], 81 | "metadata": { 82 | "pycharm": { 83 | "metadata": false, 84 | "name": "#%%\n", 85 | "is_executing": false 86 | } 87 | } 88 | }, 89 | { 90 | "cell_type": "markdown", 91 | "source": [ 92 | "## 4.2: Analyzing Recursive Algorithms\n", 93 | "\n", 94 | "With a recursive algorithm, we will account for each operation that is performed based upon the particular activation of\n", 95 | "the function that manages the flow of control at the time it is executed. Stated another way, for each invocation of the function, we only\n", 96 | "account for the number of operations that are performed within the body of that activation. We can then account for the overall number of operations that are executed as part of the\n", 97 | "recursive algorithm by taking the sum, over all activations, of the number of operations that take place during each individual activation.\n", 98 | "\n", 99 | "In general, we may rely on the intuition afforded by recursion trace in recognizing how many recursive activations occur, and how the parameterization that occur within the body of that activation.\n", 100 | "However, each of these recursive algorithms has a unique structure and form.\n", 101 | "\n", 102 | "#### Performing a Binary Search\n", 103 | "\n", 104 | "Considering the running time of the binary search algorithm, we observe that a constant number of primitive operations are executed \n", 105 | "at each recursive call of method of a binary search. Hence, the running time is proportional to the number of recursive calls performed.\n", 106 | "\n", 107 | "##### Proposition: The binary search algorithm runs in $O(\\log n)$ rimte for a sorted sequence with $n$ elements.\n" 108 | ], 109 | "metadata": { 110 | "pycharm": { 111 | "metadata": false, 112 | "name": "#%% md\n" 113 | } 114 | } 115 | }, 116 | { 117 | "cell_type": "markdown", 118 | "source": [ 119 | "## 4.3: Recursion Run Amok\n", 120 | "\n", 121 | "Let's see following recursion method" 122 | ], 123 | "metadata": { 124 | "pycharm": { 125 | "metadata": false, 126 | "name": "#%% md\n" 127 | } 128 | } 129 | }, 130 | { 131 | "cell_type": "code", 132 | "execution_count": null, 133 | "outputs": [], 134 | "source": [ 135 | "def unique(S, start, stop):\n", 136 | " \"\"\"Return True if there are no duplicate element in slicke S[start:stop]\"\"\"\n", 137 | " if stop-start <= 1: return True\n", 138 | " elif not unique(S, start, stop-1): return False\n", 139 | " elif not unique(S, start+1, stop): return False\n", 140 | " else: return S[start] != S[stop-1]" 141 | ], 142 | "metadata": { 143 | "pycharm": { 144 | "metadata": false, 145 | "name": "#%%\n" 146 | } 147 | } 148 | }, 149 | { 150 | "cell_type": "markdown", 151 | "source": [ 152 | "\n", 153 | "Unfortunately, this is a terribly inefficient use of recursion. In general case, the important observation is that a single call to `unique` for \n", 154 | "a problem of size $n$ may result in two recursive calls on problems of size $n-1$. Those two calls with size $n-1$ could in turn result in four calls with a \n", 155 | "\n", 156 | "range of size $n-2$, and thus eight calls with size $n-3$ and so on. Thus in the worst case, the total number of function \n", 157 | "call is given by the geometric summation\n", 158 | "\n", 159 | "$$1 + 2 + 4 + \\cdots + 2^{(n-1)}$$\n", 160 | "\n", 161 | "Thus, the running time of the function is $O(2^n)$.\n", 162 | "\n", 163 | "#### An Inefficient Recursion for Computing Fibonacci Numbers\n" 164 | ], 165 | "metadata": { 166 | "pycharm": { 167 | "metadata": false, 168 | "name": "#%% md\n" 169 | } 170 | } 171 | }, 172 | { 173 | "cell_type": "code", 174 | "execution_count": 39, 175 | "outputs": [], 176 | "source": [ 177 | "def bad_fibonacci(n):\n", 178 | " counter(bad_fibonacci)\n", 179 | " if n <= 1:\n", 180 | " return n\n", 181 | " else:\n", 182 | " return bad_fibonacci(n-2) + bad_fibonacci(n-1)" 183 | ], 184 | "metadata": { 185 | "pycharm": { 186 | "metadata": false, 187 | "name": "#%%\n", 188 | "is_executing": false 189 | } 190 | } 191 | }, 192 | { 193 | "cell_type": "code", 194 | "execution_count": 47, 195 | "outputs": [], 196 | "source": [ 197 | "def counter(func):\n", 198 | " func.count += 1" 199 | ], 200 | "metadata": { 201 | "pycharm": { 202 | "metadata": false, 203 | "name": "#%%\n", 204 | "is_executing": false 205 | } 206 | } 207 | }, 208 | { 209 | "cell_type": "code", 210 | "execution_count": 49, 211 | "outputs": [], 212 | "source": [ 213 | "num_called = list()\n", 214 | "for i in range(30):\n", 215 | " bad_fibonacci.count = 0\n", 216 | " bad_fibonacci(i)\n", 217 | " num_called.append(bad_fibonacci.count)" 218 | ], 219 | "metadata": { 220 | "pycharm": { 221 | "metadata": false, 222 | "name": "#%%\n", 223 | "is_executing": false 224 | } 225 | } 226 | }, 227 | { 228 | "cell_type": "code", 229 | "execution_count": 50, 230 | "outputs": [ 231 | { 232 | "data": { 233 | "text/plain": "[1,\n 1,\n 3,\n 5,\n 9,\n 15,\n 25,\n 41,\n 67,\n 109,\n 177,\n 287,\n 465,\n 753,\n 1219,\n 1973,\n 3193,\n 5167,\n 8361,\n 13529,\n 21891,\n 35421,\n 57313,\n 92735,\n 150049,\n 242785,\n 392835,\n 635621,\n 1028457,\n 1664079]" 234 | }, 235 | "metadata": {}, 236 | "output_type": "execute_result", 237 | "execution_count": 50 238 | } 239 | ], 240 | "source": [ 241 | "num_called" 242 | ], 243 | "metadata": { 244 | "pycharm": { 245 | "metadata": false, 246 | "name": "#%%\n", 247 | "is_executing": false 248 | } 249 | } 250 | }, 251 | { 252 | "cell_type": "markdown", 253 | "source": [ 254 | "You can see this approach requires incredibly inefficient!\n", 255 | "\n", 256 | "#### An Efficient Recursion for Computing Fibonacci Numbers\n", 257 | "\n", 258 | "We can compute Fibonacci much more efficiently using a recursion in which each invocation makes only one \n", 259 | "recursive call. To do so, we need to redefine the expectations of the function.\n" 260 | ], 261 | "metadata": { 262 | "pycharm": { 263 | "metadata": false, 264 | "name": "#%% md\n" 265 | } 266 | } 267 | }, 268 | { 269 | "cell_type": "code", 270 | "execution_count": 54, 271 | "outputs": [], 272 | "source": [ 273 | "def good_fibonacci(n):\n", 274 | " counter(good_fibonacci)\n", 275 | " if n<= 1:\n", 276 | " return n, 0\n", 277 | " else:\n", 278 | " (a, b) = good_fibonacci(n-1)\n", 279 | " return a+b, a" 280 | ], 281 | "metadata": { 282 | "pycharm": { 283 | "metadata": false, 284 | "name": "#%%\n", 285 | "is_executing": false 286 | } 287 | } 288 | }, 289 | { 290 | "cell_type": "code", 291 | "execution_count": 55, 292 | "outputs": [], 293 | "source": [ 294 | "num_called = list()\n", 295 | "for i in range(30):\n", 296 | " good_fibonacci.count = 0\n", 297 | " good_fibonacci(i)\n", 298 | " num_called.append(good_fibonacci.count)" 299 | ], 300 | "metadata": { 301 | "pycharm": { 302 | "metadata": false, 303 | "name": "#%%\n", 304 | "is_executing": false 305 | } 306 | } 307 | }, 308 | { 309 | "cell_type": "code", 310 | "execution_count": 56, 311 | "outputs": [ 312 | { 313 | "data": { 314 | "text/plain": "[1,\n 1,\n 2,\n 3,\n 4,\n 5,\n 6,\n 7,\n 8,\n 9,\n 10,\n 11,\n 12,\n 13,\n 14,\n 15,\n 16,\n 17,\n 18,\n 19,\n 20,\n 21,\n 22,\n 23,\n 24,\n 25,\n 26,\n 27,\n 28,\n 29]" 315 | }, 316 | "metadata": {}, 317 | "output_type": "execute_result", 318 | "execution_count": 56 319 | } 320 | ], 321 | "source": [ 322 | "num_called" 323 | ], 324 | "metadata": { 325 | "pycharm": { 326 | "metadata": false, 327 | "name": "#%%\n", 328 | "is_executing": false 329 | } 330 | } 331 | }, 332 | { 333 | "cell_type": "markdown", 334 | "source": [ 335 | "You can see `good_fibonacci` requires far less computation for getting same value!\n", 336 | "\n", 337 | "The `bad_fibonacci` function uses exponential time. But `good_fibonacci` takes $O(n)$ time. Each recursive call to \n", 338 | "`good_fibonacci` decreases the argument $n$ by $1$; therefore, a recursion trace includes a series of $n$ function calls. Because the\n", 339 | "nonrecursive work for each call uses constant time, the overall computation executes in $O(n)$ time.\n", 340 | "\n", 341 | "### 4.3.1 Maximum Recursive Depth in Python\n", 342 | "\n", 343 | "Another danger in the misuse of recursion is known as **infinite recursion**. If each recursive call amkes another recursive call, without ever\n", 344 | " reaching a base case, then we have an infinite series of such calls. This is a fatal error. An infinite recursion cna quickly swamp computing resources, \n", 345 | " not only due to rapid use of the CPU, but because each successive call create an activation record requiring additional memory.\n", 346 | " \n", 347 | "Python itself limits the overall number of function activations that can be simultaneously active. The precise value of this limit depend upon \n", 348 | " the Python distribution, but a typical default value is 1000. If this limit is reached the Python interpreter raises a `RuntimeError` with a message \n", 349 | " `maximum recursion depth exceeded`.\n", 350 | "\n", 351 | "You can dynamically reconfigure the default recursive limit as follows:\n" 352 | ], 353 | "metadata": { 354 | "pycharm": { 355 | "metadata": false, 356 | "name": "#%% md\n" 357 | } 358 | } 359 | }, 360 | { 361 | "cell_type": "code", 362 | "execution_count": 57, 363 | "outputs": [], 364 | "source": [ 365 | "import sys\n", 366 | "sys.setrecursionlimit(10000) # change limit to 10000" 367 | ], 368 | "metadata": { 369 | "pycharm": { 370 | "metadata": false, 371 | "name": "#%%\n", 372 | "is_executing": false 373 | } 374 | } 375 | }, 376 | { 377 | "cell_type": "markdown", 378 | "source": [ 379 | "## 4.4 Further Examples of Recursion\n", 380 | "\n", 381 | "* If a recursive call starts at most one other, we call this a ***linear recursion***.\n", 382 | "* If a recursive call start two others, we call this a ***binary recursion***.\n", 383 | "* If a recursive call may start three or more others, this is ***multiple recursion***.\n", 384 | "\n", 385 | "### 4.4.1 Linear Recursion\n", 386 | "\n", 387 | "If a recursive function is designed so that each invocation of the body makes at most one new recursive call, this is known as ***linear recursion***.\n", 388 | "A consequence of the definition of linear recursion is that any recursion trace will appear as a single sequence of calls. Note that\n", 389 | " the *linear recursion* terminology reflects the structure of the recursion trace, not the asymptotic analysis of the running time; for example, we have seen that \n", 390 | " binary search runs in $O(\\log n)$ time.\n", 391 | " \n", 392 | "#### Summing the Elements of a Sequence Recursively" 393 | ], 394 | "metadata": { 395 | "pycharm": { 396 | "metadata": false, 397 | "name": "#%% md\n" 398 | } 399 | } 400 | }, 401 | { 402 | "cell_type": "code", 403 | "execution_count": 58, 404 | "outputs": [ 405 | { 406 | "data": { 407 | "text/plain": "15" 408 | }, 409 | "metadata": {}, 410 | "output_type": "execute_result", 411 | "execution_count": 58 412 | } 413 | ], 414 | "source": [ 415 | "def linear_sum(S, n):\n", 416 | " \"\"\"Return the sum of the first n numbers of sequence S.\"\"\"\n", 417 | " if n == 0:\n", 418 | " return 0\n", 419 | " else:\n", 420 | " return linear_sum(S, n-1) + S[n-1]\n", 421 | "\n", 422 | "linear_sum([5, 2, 3, 5, 1, 20], 4)" 423 | ], 424 | "metadata": { 425 | "pycharm": { 426 | "metadata": false, 427 | "name": "#%%\n", 428 | "is_executing": false 429 | } 430 | } 431 | }, 432 | { 433 | "cell_type": "markdown", 434 | "source": [ 435 | "#### Reversing a Sequence with Recursion" 436 | ], 437 | "metadata": { 438 | "pycharm": { 439 | "metadata": false, 440 | "name": "#%% md\n" 441 | } 442 | } 443 | }, 444 | { 445 | "cell_type": "code", 446 | "execution_count": 62, 447 | "outputs": [ 448 | { 449 | "data": { 450 | "text/plain": "[6, 5, 4, 3, 2, 1]" 451 | }, 452 | "metadata": {}, 453 | "output_type": "execute_result", 454 | "execution_count": 62 455 | } 456 | ], 457 | "source": [ 458 | "def reverse(S, start, stop):\n", 459 | " \"\"\"Reverse elements in implicit slice S[start:stop].\"\"\"\n", 460 | " if start < stop - 1:\n", 461 | " S[start], S[stop-1] = S[stop-1], S[start]\n", 462 | " reverse(S, start+1, stop-1)\n", 463 | "\n", 464 | "x = [1, 2, 3, 4, 5, 6]\n", 465 | "reverse(x, 0, 6)\n", 466 | "x" 467 | ], 468 | "metadata": { 469 | "pycharm": { 470 | "metadata": false, 471 | "name": "#%%\n", 472 | "is_executing": false 473 | } 474 | } 475 | }, 476 | { 477 | "cell_type": "markdown", 478 | "source": [ 479 | "#### Recursive Algorithms for Computing Powers\n", 480 | "\n", 481 | "As another interesting example of the use of linear recursion, we consider the problem of raising a number $x$ to an \n", 482 | "arbitrary nonnegative integer, $n$. That is, we wish to compute the **power function** defined as $\\text{power}(x, n) = x^n$.\n" 483 | ], 484 | "metadata": { 485 | "pycharm": { 486 | "metadata": false, 487 | "name": "#%% md\n" 488 | } 489 | } 490 | }, 491 | { 492 | "cell_type": "code", 493 | "execution_count": 72, 494 | "outputs": [ 495 | { 496 | "name": "stdout", 497 | "text": [ 498 | "1099511627776\n# Called: 41\n" 499 | ], 500 | "output_type": "stream" 501 | } 502 | ], 503 | "source": [ 504 | "def power(x, n):\n", 505 | " \"\"\"Compute the value x**n for integer n.\"\"\"\n", 506 | " counter(power)\n", 507 | " if n == 0:\n", 508 | " return 1\n", 509 | " else:\n", 510 | " return x * power(x, n-1)\n", 511 | "\n", 512 | "power.count = 0\n", 513 | "print(power(2, 40))\n", 514 | "print(\"# Called: \", power.count)" 515 | ], 516 | "metadata": { 517 | "pycharm": { 518 | "metadata": false, 519 | "name": "#%%\n", 520 | "is_executing": false 521 | } 522 | } 523 | }, 524 | { 525 | "cell_type": "markdown", 526 | "source": [ 527 | "A recursive calls to this version runs in $O(n)$ time. ITs recursion trace has structure very similar to that of the factorial function.\n", 528 | "\n", 529 | "However, there is a much faster way to compute the power function using an alternative definition that employs a squaring technique.\n", 530 | "\n", 531 | "$$\n", 532 | "\\text{power}(x, n) = \n", 533 | "\\begin{cases}\n", 534 | "1 & \\text{if } n = 0 \\\\\n", 535 | "x \\cdot (\\text{power}(x, \\left\\lfloor \\frac{n}{2} \\right\\rfloor ))^2& \\text{if } n > 0 \\ \\text{is odd}\\\\ \n", 536 | "(\\text{power}(x, \\left\\lfloor \\frac{n}{2} \\right\\rfloor ))^2& \\text{if } n > 0 \\ \\text{is even}\\\\ \n", 537 | "\\end{cases}$$\n" 538 | ], 539 | "metadata": { 540 | "pycharm": { 541 | "metadata": false, 542 | "name": "#%% md\n" 543 | } 544 | } 545 | }, 546 | { 547 | "cell_type": "code", 548 | "execution_count": 73, 549 | "outputs": [ 550 | { 551 | "name": "stdout", 552 | "text": [ 553 | "1099511627776\n# Called: 7\n" 554 | ], 555 | "output_type": "stream" 556 | } 557 | ], 558 | "source": [ 559 | "def power(x, n):\n", 560 | " \"\"\"Compute the value x**n for integer n.\"\"\"\n", 561 | " counter(power)\n", 562 | " if n == 0:\n", 563 | " return 1\n", 564 | " else:\n", 565 | " partial = power(x, n//2)\n", 566 | " result = partial * partial\n", 567 | " if n%2 == 1:\n", 568 | " result *= x\n", 569 | " return result\n", 570 | "\n", 571 | "power.count = 0\n", 572 | "print(power(2, 40))\n", 573 | "print(\"# Called: \", power.count)" 574 | ], 575 | "metadata": { 576 | "pycharm": { 577 | "metadata": false, 578 | "name": "#%%\n", 579 | "is_executing": false 580 | } 581 | } 582 | }, 583 | { 584 | "cell_type": "markdown", 585 | "source": [ 586 | "To analyze the running time of the revised algorithm, we observe taht the exponent in each recursive call of function `power(x,n)` is \n", 587 | "at most half of the preceding exponent. As we saw with the analysis of binary search, the number of times that we can divide $n$ in half before getting to one or less is $O(\\log n)$.\n", 588 | "Therefore, our new formulation of the `power` function results in $O(\\log n)$ recursive calls. Each individual activation of the functions uses $O(1)$ operations (excluding the recursive calls), and so the \n", 589 | " total number of operations for computing `power(x,n)` is $O(\\log n)$. THis is a significant improvement over the original $O(n)$-time algorihtm.\n", 590 | " \n", 591 | " Since the recursive depth of the improved ve3rsion is $O(\\log n)$, its memory usage is $O(\\log n)$ as well.\n" 592 | ], 593 | "metadata": { 594 | "pycharm": { 595 | "metadata": false, 596 | "name": "#%% md\n" 597 | } 598 | } 599 | }, 600 | { 601 | "cell_type": "markdown", 602 | "source": [ 603 | "### 4.4.2: Binary Recursion\n", 604 | "\n", 605 | "When a function makes two recursive calls, we say that it uses ***binary recursion***.\n" 606 | ], 607 | "metadata": { 608 | "pycharm": { 609 | "metadata": false, 610 | "name": "#%% md\n", 611 | "is_executing": false 612 | } 613 | } 614 | }, 615 | { 616 | "cell_type": "code", 617 | "execution_count": 80, 618 | "outputs": [ 619 | { 620 | "data": { 621 | "text/plain": "1225" 622 | }, 623 | "metadata": {}, 624 | "output_type": "execute_result", 625 | "execution_count": 80 626 | } 627 | ], 628 | "source": [ 629 | "def binary_sum(S, start, stop):\n", 630 | " \"\"\"Return the sum of the numbers in implicit slice S[start:stop].\"\"\"\n", 631 | " if start >= stop:\n", 632 | " return 0\n", 633 | " elif start == stop - 1:\n", 634 | " return S[start]\n", 635 | " else:\n", 636 | " mid = (start + stop) // 2\n", 637 | " return binary_sum(S, start, mid) + binary_sum(S, mid, stop)\n", 638 | "\n", 639 | "binary_sum(list(range(100)), 0, 50)" 640 | ], 641 | "metadata": { 642 | "pycharm": { 643 | "metadata": false, 644 | "name": "#%%\n", 645 | "is_executing": false 646 | } 647 | } 648 | }, 649 | { 650 | "cell_type": "markdown", 651 | "source": [ 652 | "The size of the range is divided in half at each recursive call, and so the depth of the recursion is $1 + \\log_2 n$. Therefore, \n", 653 | "`binary_sum` uses $O(\\log n)$ amount of additional space, which is a big improvement over $O(\\log n)$ space used by the `linear_sum` function. \n", 654 | "However, the running time of `binary_sum` is $O(n)$, as there are $2n-1$ function calls, each requiring constant time.\n" 655 | ], 656 | "metadata": { 657 | "pycharm": { 658 | "metadata": false, 659 | "name": "#%% md\n" 660 | } 661 | } 662 | }, 663 | { 664 | "cell_type": "markdown", 665 | "source": [ 666 | "### 4.4.3 Multiple Recursion\n", 667 | "\n", 668 | "Generalizing from binary recursion, we define ***multiple recursion*** as a process in which a function may make more than two recursive calls.\n" 669 | ], 670 | "metadata": { 671 | "pycharm": { 672 | "metadata": false, 673 | "name": "#%% md\n" 674 | } 675 | } 676 | }, 677 | { 678 | "cell_type": "markdown", 679 | "source": [ 680 | "## 4.5 Designing Recursive Algorithms\n", 681 | "\n", 682 | "In general, an algorithm that uses recursion typically has the following form:\n", 683 | "\n", 684 | "* Test for base cases\n", 685 | "\n", 686 | " We begin by testing for a set of base cases (there should be at least one). These base cases should be defined so that every possible chain of recursive calls will eventually reach a base case, \n", 687 | " and the handling of each base case should not use recursion.\n", 688 | " \n", 689 | "* Recur. \n", 690 | "\n", 691 | " If not a base case, we perform one or more recursive calls. This recursive step may involve a test that decides which of several possible recursive \n", 692 | " calls to make. We should define each possible recursive call so that it makes progress towards a base case.\n", 693 | " \n", 694 | "#### Parameterizing a Recursion\n", 695 | "To design a recursive algorithm for a given problem, it is useful to think of the different ways we might define subproblems that have the same general structure as the original problem. \n", 696 | "If one has difficulty finding the repetitive structure needed to design a recursive algorithm, it is sometimes useful to work out the problem on a few concrete \n", 697 | "examples to see how the subproblems should be defined.\n", 698 | "\n", 699 | "A successful recursive design sometimes requires that we redefine the original problem to facilitate similar-looking subproblems. \n", 700 | "Often, this involved reparameterizing the signature of the function. For example, when performing a binary search in a sequence, a natural function signature for a caller \n", 701 | "would appear as `binary_search(data, target)`. However, we defined our function with calling signature `binary_search(data, target, low, high)` using the additional parameters to \n", 702 | "demarcate sublists as the recursion proceeds. This change in parameterization is critical for binary search. If we had insisted on the cleaner signature, \n", 703 | "`bianry_search(data, target)`, the only way to invoke a search on half the list would have been to make a new list instance with only those elements to send as the first parameter. However, making a copy of half the list would already \n", 704 | "take $O(n)$ time, negating the whole benefit of the binary search algorithm.\n", 705 | "\n", 706 | "If we wished to provide a cleaner public interface to an algorithm like binary search, without bothering a user with extra \n", 707 | "parameters, a standard technique is to make one function for public use with the cleaner interface, such as `binary_search(data, target)`, and then having its body invoke a nonpublic utility function having the \n", 708 | "desired recursive parameters.\n" 709 | ], 710 | "metadata": { 711 | "pycharm": { 712 | "metadata": false, 713 | "name": "#%% md\n" 714 | } 715 | } 716 | }, 717 | { 718 | "cell_type": "markdown", 719 | "source": [ 720 | "## 4.6 Eliminating Tail Recursion\n", 721 | "\n", 722 | "The main benefit of a recursive approach to algorithm design is that it allows us to succintly take adavantage of a repetitive structure present in many problems.\n", 723 | "By making our algorithm description exploit the repetitive structure in a recursive way, we can often avoid complex case analyes and nested loops. \n", 724 | "This approach can lead to more readable algorithm descriptions, while stil being quite efficient.\n", 725 | "\n", 726 | "However, the usefulness of recursion comes at a modest cost. In particular the Python interpreter must maintain activation recors that keep track of the state of each nested cell. \n", 727 | "When computer memory is at a premium, it is useful in some cases to be able to derive nonrecursive algorithms from recursive ones.\n", 728 | "\n", 729 | "In general, we can use the stack data structure to convert a recursive algorithm into a nonrecursive algorithm by managing the nesting of the recursive structure ourselves, rather than relying on the \n", 730 | "interpreter to do so. Although this only shifts the memory usage from the interpreter to our stack, we may be able to reduce the memory usage by storing only minimal information necessary.\n", 731 | "\n", 732 | "Even better, some forms of recursion can be eliminated without any use of axillary memory. A notable such form is known as **tail recursion**. \n", 733 | "A recursion last operation in that context, with the return value of the recursive call (if any) immediately returned by the enclosing recursion. By necessity, a tail recursion must be a linear recursion (since there is no way to make a second recursive call if you must immediately return the result of the first).\n", 734 | "\n", 735 | "Followings are tail recursion. You can see that last operation is same as the method itself.\n" 736 | ], 737 | "metadata": { 738 | "pycharm": { 739 | "metadata": false, 740 | "name": "#%% md\n" 741 | } 742 | } 743 | }, 744 | { 745 | "cell_type": "code", 746 | "execution_count": null, 747 | "outputs": [], 748 | "source": [ 749 | "def binary_search(data, target, low, high):\n", 750 | " \"\"\"\n", 751 | " Return True if target is found in indicated portion of a Python list\n", 752 | " \n", 753 | " The search only considers the portion from data[low] to data[high] inclusive.\n", 754 | " \n", 755 | " :param data: sequence that value will be searched\n", 756 | " :param target: value to find\n", 757 | " :param low: lower bound of sequence's index\n", 758 | " :param high: upper bound of sequence's index\n", 759 | " :return: \n", 760 | " \"\"\"\n", 761 | " \n", 762 | " if low > high:\n", 763 | " return False\n", 764 | " else:\n", 765 | " mid = (low + high) // 2\n", 766 | " if target == data[mid]:\n", 767 | " return True\n", 768 | " elif target < data[mid]:\n", 769 | " return binary_search(data, target, low, mid -1)\n", 770 | " else:\n", 771 | " return binary_search(data, target, mid+1, high)\n", 772 | " \n", 773 | "def reverse(S, start, stop):\n", 774 | " \"\"\"Reverse elements in implicit slice S[start:stop].\"\"\"\n", 775 | " if start < stop - 1:\n", 776 | " S[start], S[stop-1] = S[stop-1], S[start]\n", 777 | " reverse(S, start+1, stop-1)\n" 778 | ], 779 | "metadata": { 780 | "collapsed": false, 781 | "pycharm": { 782 | "name": "#%%\n" 783 | } 784 | } 785 | }, 786 | { 787 | "cell_type": "markdown", 788 | "source": [ 789 | "Any tail recursion can be reimplemented nonrecursively by enclosing the body in a loop for repetition, and replacing a \n", 790 | "recursive call with new parameters by a reassignment of the existing parameters to those values. As a tangible example, our \n", 791 | "`binary_search` function can be reimplemented as follow:\n" 792 | ], 793 | "metadata": { 794 | "collapsed": false, 795 | "pycharm": { 796 | "name": "#%% md\n" 797 | } 798 | } 799 | }, 800 | { 801 | "cell_type": "code", 802 | "execution_count": 7, 803 | "outputs": [ 804 | { 805 | "data": { 806 | "text/plain": "True" 807 | }, 808 | "metadata": {}, 809 | "output_type": "execute_result", 810 | "execution_count": 7 811 | } 812 | ], 813 | "source": [ 814 | "def binary_search_iterative(data, target):\n", 815 | " \"\"\"Return True if target is found in the given Python list.\"\"\"\n", 816 | " low = 0\n", 817 | " high = len(data) - 1\n", 818 | " while low <= high:\n", 819 | " mid = (low + high) // 2\n", 820 | " if target == data[mid]:\n", 821 | " return True\n", 822 | " elif target < data[mid]:\n", 823 | " high = mid - 1\n", 824 | " else:\n", 825 | " low = mid + 1\n", 826 | " return False\n", 827 | " \n", 828 | "binary_search_iterative(list(range(100)), 89)" 829 | ], 830 | "metadata": { 831 | "collapsed": false, 832 | "pycharm": { 833 | "name": "#%%\n", 834 | "is_executing": false 835 | } 836 | } 837 | }, 838 | { 839 | "cell_type": "markdown", 840 | "source": [ 841 | "\n", 842 | "We can similarly develop a nonrecursive implementation of the original recursive `reverse` method.\n" 843 | ], 844 | "metadata": { 845 | "collapsed": false, 846 | "pycharm": { 847 | "name": "#%% md\n" 848 | } 849 | } 850 | }, 851 | { 852 | "cell_type": "code", 853 | "execution_count": 9, 854 | "outputs": [ 855 | { 856 | "data": { 857 | "text/plain": "[19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]" 858 | }, 859 | "metadata": {}, 860 | "output_type": "execute_result", 861 | "execution_count": 9 862 | } 863 | ], 864 | "source": [ 865 | "def reverse_iterative(S):\n", 866 | " \"\"\"Reverse elements in sequence S.\"\"\"\n", 867 | " start, stop = 0, len(S)\n", 868 | " while start < stop - 1:\n", 869 | " S[start], S[stop-1] = S[stop-1], S[start]\n", 870 | " start, stop = start +1, stop -1\n", 871 | "x = list(range(20))\n", 872 | "reverse_iterative(x)\n", 873 | "x" 874 | ], 875 | "metadata": { 876 | "collapsed": false, 877 | "pycharm": { 878 | "name": "#%%\n", 879 | "is_executing": false 880 | } 881 | } 882 | }, 883 | { 884 | "cell_type": "markdown", 885 | "source": [ 886 | "\n", 887 | "Many other linear recursions can be expressed quite efficiently with iteration, even if they were not formally tail recursions. \n", 888 | "For example, there are trivial nonrecursive implementations for computing factorials, summing elements of a sequence, or computing Fibonacci numbers efficiently.\n" 889 | ], 890 | "metadata": { 891 | "collapsed": false, 892 | "pycharm": { 893 | "name": "#%% md\n" 894 | } 895 | } 896 | } 897 | ], 898 | "metadata": { 899 | "language_info": { 900 | "codemirror_mode": { 901 | "name": "ipython", 902 | "version": 2 903 | }, 904 | "file_extension": ".py", 905 | "mimetype": "text/x-python", 906 | "name": "python", 907 | "nbconvert_exporter": "python", 908 | "pygments_lexer": "ipython2", 909 | "version": "2.7.6" 910 | }, 911 | "kernelspec": { 912 | "name": "python3", 913 | "language": "python", 914 | "display_name": "Python 3" 915 | }, 916 | "stem_cell": { 917 | "cell_type": "raw", 918 | "source": "", 919 | "metadata": { 920 | "pycharm": { 921 | "metadata": false 922 | } 923 | } 924 | }, 925 | "pycharm": { 926 | "stem_cell": { 927 | "cell_type": "raw", 928 | "source": [], 929 | "metadata": { 930 | "collapsed": false 931 | } 932 | } 933 | } 934 | }, 935 | "nbformat": 4, 936 | "nbformat_minor": 0 937 | } 938 | -------------------------------------------------------------------------------- /Contents/Chapter_06.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Chapter 06: Stacks, Queues, and Deques" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "## 6.1 Stacks" 15 | ] 16 | }, 17 | { 18 | "cell_type": "markdown", 19 | "metadata": {}, 20 | "source": [ 21 | "A **stack** is a collection of objects that are inserted and removed according to the ***last-in, first-out (LIFO)*** principle." 22 | ] 23 | }, 24 | { 25 | "cell_type": "markdown", 26 | "metadata": {}, 27 | "source": [ 28 | "#### Example 6.1:\n", 29 | "\n", 30 | "Internet web browsers store the addresses of recently visited sites in stack. Each time a user visits a new site, that site's address is \"pushed\" onto the stack of addresses. The browser then allows the user to \"pop\" back to previously visited sites using the \"back\" button." 31 | ] 32 | }, 33 | { 34 | "cell_type": "markdown", 35 | "metadata": {}, 36 | "source": [ 37 | "#### Example 6.2:\n", 38 | "Text editors usually provide an \"undo\" mechanism that cancels recent editing operations and reverts to former states of a document. This undo operation can be accomplished by keeping text changes in a stack." 39 | ] 40 | }, 41 | { 42 | "cell_type": "markdown", 43 | "metadata": {}, 44 | "source": [ 45 | "### 6.1.1 The Stack Abstract Data Type" 46 | ] 47 | }, 48 | { 49 | "cell_type": "markdown", 50 | "metadata": {}, 51 | "source": [ 52 | "Fomally, a stack is an abstrat data type (ADT) such that an instance $\\mathcal{S}$ supports the folloiwng two methods:" 53 | ] 54 | }, 55 | { 56 | "cell_type": "markdown", 57 | "metadata": {}, 58 | "source": [ 59 | "* `S.push(e)`: Add element `e` to the top of stack `S`.\n", 60 | "* `S.pop()`: Remove and return the top element form the stack `S`; an error occurs if the stack is empty." 61 | ] 62 | }, 63 | { 64 | "cell_type": "markdown", 65 | "metadata": {}, 66 | "source": [ 67 | "Additionally, let us define the following accessor methods for convenience:\n", 68 | "\n", 69 | "* `S.top()`: Return a reference to the top element of stack `S`, without removing itl an error occurs if the stack is empty.\n", 70 | "* `S.is_empty()`: Return `True` if stack `S` does not contain any elements.\n", 71 | "* `len(S)`: Return the number of elements in stack `S`" 72 | ] 73 | }, 74 | { 75 | "cell_type": "markdown", 76 | "metadata": {}, 77 | "source": [ 78 | "By convention, we assume that a newly created stack is empty, and that there is no a priori bound on the capacity of the stack. Elements added to the stack can have arbitrary type." 79 | ] 80 | }, 81 | { 82 | "cell_type": "markdown", 83 | "metadata": {}, 84 | "source": [ 85 | "### 6.1.2 Simple Array-Based Stack Implementation" 86 | ] 87 | }, 88 | { 89 | "cell_type": "markdown", 90 | "metadata": {}, 91 | "source": [ 92 | "The `list` class already supports adding an element to the end with `append` method, and removing the last element with the `pop` method, so it is natural to align the top of the stack at the end of the list.\n", 93 | "\n", 94 | "Although a aprogrammer could directly use the `list` class in place ofa formal stack class, lists also include behaviors that would break the abstraction that the stack ADT represents. ALso, the terminology used by the `list` class does not precisely align with traditional nomenclature for a stack ADT, in particular the distinction between `append` and `push`. Instead, we demonstrate how to use a list for internal storage while providing a public interface consistent with a stack." 95 | ] 96 | }, 97 | { 98 | "cell_type": "markdown", 99 | "metadata": {}, 100 | "source": [ 101 | "#### The Adapter Pattern\n", 102 | "\n", 103 | "The ***adapter*** design pattern applies to any context where we effectively want to modify an existing class so that its methods match those of a related, but different, class or interface. One general way to apply the adapter pattern is to define a new class in such a way that it contains an instance of the existing class as a hidden field, and then to implement each method of the new class using methods of this hidden instance variable. By applying the adapter pattern in this way, we have created a new class that performs some of the same functions as an existing class, but repackaged in a more convenient way. In the context of tehe stack ADT, we c an adapt Python's list class using the correspondence shown in \n", 104 | "\n", 105 | "|stack Method|Realization with Python List|\n", 106 | "|---|---|\n", 107 | "|`S.push(e)`|`L.append(e)`|\n", 108 | "|`S.pop()`|`L.pop()`|\n", 109 | "|`S.top()`|`L[-1]`|\n", 110 | "|`S.is_empty()`|`len(L)==0`|\n", 111 | "|`len(S)`|`len(L)`|" 112 | ] 113 | }, 114 | { 115 | "cell_type": "code", 116 | "execution_count": 4, 117 | "metadata": {}, 118 | "outputs": [], 119 | "source": [ 120 | "class Empty(Exception):\n", 121 | " pass" 122 | ] 123 | }, 124 | { 125 | "cell_type": "code", 126 | "execution_count": 8, 127 | "metadata": {}, 128 | "outputs": [], 129 | "source": [ 130 | "class ArrayStack:\n", 131 | " \"\"\"LIFO Stack implementatino using a Python list as underlying storage.\"\"\"\n", 132 | " \n", 133 | " def __init__(self):\n", 134 | " self._data = []\n", 135 | " \n", 136 | " def __len__(self):\n", 137 | " return len(self._data)\n", 138 | " \n", 139 | " def is_empty(self):\n", 140 | " return len(self._data) == 0\n", 141 | " \n", 142 | " def push(self, e):\n", 143 | " self._data.append(e)\n", 144 | " \n", 145 | " def top(self):\n", 146 | " if self.is_empty():\n", 147 | " raise Empty('Stack is empty')\n", 148 | " return self._data[-1]\n", 149 | " \n", 150 | " def pop(self):\n", 151 | " if self.is_empty():\n", 152 | " raise Empty('Stack is empty')\n", 153 | " return self._data.pop()" 154 | ] 155 | }, 156 | { 157 | "cell_type": "markdown", 158 | "metadata": {}, 159 | "source": [ 160 | "#### Analyzing the Array-Based Stack Implementation\n", 161 | "\n", 162 | "|Operation|Running Time|\n", 163 | "|---|---|\n", 164 | "|`S.push(e)`|$O(1)$ (amozrtized)|\n", 165 | "|`S.pop()`|$O(1)$ (amortized)|\n", 166 | "|`S.top()`|$O(1)$|\n", 167 | "|`S.is_empty()`|$O(1)$|\n", 168 | "|`len(S)`|$O(1)$|" 169 | ] 170 | }, 171 | { 172 | "cell_type": "markdown", 173 | "metadata": {}, 174 | "source": [ 175 | "### 6.1.3 Reserving Data Using a Stack" 176 | ] 177 | }, 178 | { 179 | "cell_type": "markdown", 180 | "metadata": {}, 181 | "source": [ 182 | "As a consequence of the LIFO protocol, a stack can be used as a general tool to reserve a data sequence." 183 | ] 184 | }, 185 | { 186 | "cell_type": "code", 187 | "execution_count": 9, 188 | "metadata": {}, 189 | "outputs": [], 190 | "source": [ 191 | "def reverse_file(filename):\n", 192 | " S = ArrayStack()\n", 193 | " original = open(filename)\n", 194 | " for line in original:\n", 195 | " S.push(line.rstrip('\\n'))\n", 196 | " original.close()\n", 197 | " \n", 198 | " output = open(filename, 'w')\n", 199 | " while not S.is_empty():\n", 200 | " output.write(S.pop() + '\\n')\n", 201 | " output.close()" 202 | ] 203 | }, 204 | { 205 | "cell_type": "markdown", 206 | "metadata": {}, 207 | "source": [ 208 | "### 6.1.4 Matching Paratheses and HTML Tags" 209 | ] 210 | }, 211 | { 212 | "cell_type": "markdown", 213 | "metadata": {}, 214 | "source": [ 215 | "#### An algorithm for Matching Delimiters" 216 | ] 217 | }, 218 | { 219 | "cell_type": "code", 220 | "execution_count": 35, 221 | "metadata": {}, 222 | "outputs": [], 223 | "source": [ 224 | "def is_matched(expr):\n", 225 | " \n", 226 | " lefty = \"({[\"\n", 227 | " righty = \")}]\"\n", 228 | " \n", 229 | " S = ArrayStack()\n", 230 | " \n", 231 | " for c in expr:\n", 232 | " if c in lefty:\n", 233 | " S.push(c)\n", 234 | " elif c in righty:\n", 235 | " if S.is_empty():\n", 236 | " return False\n", 237 | " if righty.index(c) != lefty.index(S.pop()):\n", 238 | " return False\n", 239 | " return S.is_empty()" 240 | ] 241 | }, 242 | { 243 | "cell_type": "code", 244 | "execution_count": 36, 245 | "metadata": {}, 246 | "outputs": [ 247 | { 248 | "data": { 249 | "text/plain": [ 250 | "True" 251 | ] 252 | }, 253 | "execution_count": 36, 254 | "metadata": {}, 255 | "output_type": "execute_result" 256 | } 257 | ], 258 | "source": [ 259 | "is_matched('[(5+x)-(y+z)]')" 260 | ] 261 | }, 262 | { 263 | "cell_type": "markdown", 264 | "metadata": {}, 265 | "source": [ 266 | "#### Matching Tags in Markup Language" 267 | ] 268 | }, 269 | { 270 | "cell_type": "code", 271 | "execution_count": 37, 272 | "metadata": {}, 273 | "outputs": [], 274 | "source": [ 275 | "def is_matched_html(raw):\n", 276 | " S = ArrayStack()\n", 277 | " j = raw.find('<')\n", 278 | " \n", 279 | " while j != -1:\n", 280 | " k = raw.find('>', j+1)\n", 281 | " if k == -1:\n", 282 | " return False\n", 283 | " tag = raw[j+1:k]\n", 284 | " if not tag.startswith('/'):\n", 285 | " S.push(tag)\n", 286 | " else:\n", 287 | " if S.is_empty():\n", 288 | " return False\n", 289 | " if tag[1:] != S.pop():\n", 290 | " return False\n", 291 | " j = raw.find('<', k+1)\n", 292 | " return S.is_empty()" 293 | ] 294 | }, 295 | { 296 | "cell_type": "markdown", 297 | "metadata": {}, 298 | "source": [ 299 | "## 6.2 Queues" 300 | ] 301 | }, 302 | { 303 | "cell_type": "markdown", 304 | "metadata": {}, 305 | "source": [ 306 | "Another fundamental data structure is the ***queue***. It is a close \"cousin\" of the stack, as a queue is a collection of objects that are inserted and removed according to the ***first-in, first-out (FIFO)*** principle. That is, elements can be inserted at any time, but only the element that has been in the queue the longest can be next removed." 307 | ] 308 | }, 309 | { 310 | "cell_type": "markdown", 311 | "metadata": {}, 312 | "source": [ 313 | "### 6.2.1 The Queue Abstract Data Type" 314 | ] 315 | }, 316 | { 317 | "cell_type": "markdown", 318 | "metadata": {}, 319 | "source": [ 320 | "Formally, the queue abstract data type defines a collection that keeps objects in a sequence, where element access and deleteion are restricted to the ***first*** element in the queue, and element insertion is restricted to the back of the sequence. This restriction enforces the rule that items are inserted and deleted in a queue according to the first-in, first-out (FIFO) principle. The ***queue*** abstract data type (ADT) supports the following two fundamental mthods for a queue Q:\n" 321 | ] 322 | }, 323 | { 324 | "cell_type": "markdown", 325 | "metadata": {}, 326 | "source": [ 327 | "* `Q.enqueue(e)`: Add element `e` to the back of queue `Q`\n", 328 | "* `Q.dequeue()`: Remove and return the first element form queue `Q`; an error occurs if the queue is empty.\n", 329 | "* `Q.first()`: Return a reference to the element at the front of queue `Q`, without removing it; an error occurs if the queue is empty.\n", 330 | "* `Q.is_empty()`: Return `True` if queue `Q` does not contain any elements.\n", 331 | "* `len(Q)`: Return the number of elements in queue `Q`" 332 | ] 333 | }, 334 | { 335 | "cell_type": "markdown", 336 | "metadata": {}, 337 | "source": [ 338 | "By convention, we assume that a newly created queue is empty, and that there is no a priori bound on the capacity of the queue. Elements added to the queue can have arbitrary type." 339 | ] 340 | }, 341 | { 342 | "cell_type": "markdown", 343 | "metadata": {}, 344 | "source": [ 345 | "### 6.2.2 Array-Based Queue Implementation" 346 | ] 347 | }, 348 | { 349 | "cell_type": "code", 350 | "execution_count": 46, 351 | "metadata": {}, 352 | "outputs": [], 353 | "source": [ 354 | "class ArrayQueue:\n", 355 | " \n", 356 | " DEFAULT_CAPACITY = 10\n", 357 | " \n", 358 | " def __init__(self):\n", 359 | " self._data = [None] * ArrayQueue.DEFAULT_CAPACITY\n", 360 | " self._size = 0\n", 361 | " self._front = 0\n", 362 | " \n", 363 | " def __len__(self):\n", 364 | " return self._size\n", 365 | " \n", 366 | " def is_empty(self):\n", 367 | " return self._size == 0\n", 368 | " \n", 369 | " def first(self):\n", 370 | " if self.is_empty():\n", 371 | " raise Empty(\"Queue is empty\")\n", 372 | " return self._data[self._front]\n", 373 | " \n", 374 | " def dequeue(self):\n", 375 | " \n", 376 | " if self.is_empty():\n", 377 | " raise Empty(\"Queue is empty\")\n", 378 | " answer = self._data[self._front]\n", 379 | " self._data[self._front] = None\n", 380 | " self._front = (self._fornt +1) % len(self._data)\n", 381 | " self._size -= 1\n", 382 | " \n", 383 | " if 0 < self._size < len(self._data) // 4:\n", 384 | " self._resize(len(self._data) // 2)\n", 385 | " return answer\n", 386 | " \n", 387 | " def enqueue(self, e):\n", 388 | " if self._size == len(self._data):\n", 389 | " self._resize(2 * len(self._data))\n", 390 | " avail = (self._fornt + self._size) % len(self._data)\n", 391 | " self._data[avail] = e\n", 392 | " self._size += 1\n", 393 | " \n", 394 | " def _resize(self, cap):\n", 395 | " \n", 396 | " old = self._data\n", 397 | " self._data = [None] * cap\n", 398 | " walk = self._front\n", 399 | " for k in range(self._size):\n", 400 | " self._data[k] = old[walk]\n", 401 | " walk = (1 + walk) % len(old)\n", 402 | " self._front = 0" 403 | ] 404 | }, 405 | { 406 | "cell_type": "markdown", 407 | "metadata": {}, 408 | "source": [ 409 | "#### Analyzing the Array-Based Queue Implementation" 410 | ] 411 | }, 412 | { 413 | "cell_type": "markdown", 414 | "metadata": {}, 415 | "source": [ 416 | "|Operation|Running Time|\n", 417 | "|---|---|\n", 418 | "|`Q.enqueue(e)`|$O(1)$ (amortized)|\n", 419 | "|`Q.dequeue()`|$O(1)$ (amortized)|\n", 420 | "|`Q.first()`|$O(1)$|\n", 421 | "|`Q.is_empty()`|$O(1)$|\n", 422 | "|`len(Q)`|$O(1)$|" 423 | ] 424 | }, 425 | { 426 | "cell_type": "markdown", 427 | "metadata": {}, 428 | "source": [ 429 | "## 6.3 Double-Ended Queues" 430 | ] 431 | }, 432 | { 433 | "cell_type": "markdown", 434 | "metadata": {}, 435 | "source": [ 436 | "We next consider a queue-like data structure that supports insertion and deletion at both the front and the back of the queue. Such a structure is called a ***double-ended queue***, or ***deque***, which is usally pronounced \"deck\" to avoid confusion with the `dequeue` method of the regular queue ADT, which is pronounced like the abbreviation \"D.Q.\"" 437 | ] 438 | }, 439 | { 440 | "cell_type": "markdown", 441 | "metadata": {}, 442 | "source": [ 443 | "### 6.3.1 The Deque Abstract Data Type" 444 | ] 445 | }, 446 | { 447 | "cell_type": "markdown", 448 | "metadata": {}, 449 | "source": [ 450 | "To provide a symmetrical abstraction, the deque ADT is defined so that deque `D` supports the following methods:" 451 | ] 452 | }, 453 | { 454 | "cell_type": "markdown", 455 | "metadata": {}, 456 | "source": [ 457 | "* `D.add_first(e)`: Add element `e` to the fornt of deque `D`.\n", 458 | "* `D.add_last(e)`: Add element `e` to the back of deque `D`.\n", 459 | "* `D.delete_first()`: Remove and return the fist element from deque `D`\n", 460 | "* `D.first()`: Return the first element of deque `D`\n", 461 | "* `D.last()`: Return the last element of deque `D`\n", 462 | "* `D.is_empty()`: Return `True` if deque `D` does not contain any elements.\n", 463 | "* `len(D)`: Return the number of elements in deque `D`" 464 | ] 465 | }, 466 | { 467 | "cell_type": "markdown", 468 | "metadata": {}, 469 | "source": [ 470 | "### 6.3.2 Implementing a Deque with a Circular Array" 471 | ] 472 | }, 473 | { 474 | "cell_type": "markdown", 475 | "metadata": {}, 476 | "source": [ 477 | "Do it at Exercise P-6.32!" 478 | ] 479 | }, 480 | { 481 | "cell_type": "markdown", 482 | "metadata": {}, 483 | "source": [ 484 | "### 6.3.3 Deques in the Python Collections Module" 485 | ] 486 | }, 487 | { 488 | "cell_type": "markdown", 489 | "metadata": {}, 490 | "source": [ 491 | "An implementation fo a `deque` class is aviailable in Python's standard collections module. A summary of the most commonly used behaviors of the `collections.deque` class is given follow:" 492 | ] 493 | }, 494 | { 495 | "cell_type": "markdown", 496 | "metadata": {}, 497 | "source": [ 498 | "|`collections.deque`|Description|\n", 499 | "|---|---|\n", 500 | "|`len(D)`|number of elements|\n", 501 | "|`D.appendleft()`|add to beginning|\n", 502 | "|`D.append()`|add to end|\n", 503 | "|`D.popleft()`|remove from beginning|\n", 504 | "|`D.pop()`|remove from end|\n", 505 | "|`D[0]`|access first element|\n", 506 | "|`D[-1]`|access last element|\n", 507 | "|`D[j]`|access arbitrary entry by index|\n", 508 | "|`D[j] = val`|modify arbitrary entry by index|\n", 509 | "|`D.clear()`|clear all contents|\n", 510 | "|`D.rotate(k)`|circularly shift rightward $k$ steps|\n", 511 | "|`D.remove(e)`|remove first matching element|\n", 512 | "|`D.count(e)`|count number of matches for `e`|" 513 | ] 514 | }, 515 | { 516 | "cell_type": "markdown", 517 | "metadata": {}, 518 | "source": [ 519 | "The library `deque` constructor also supports an optional `maxlen` parameter to force a fixed-length deque. However, if a call to append at either end is invoked when the deque is full, it does not throw an error; instead, it causes one element to be dropped from the opposite side. That is, calling `appendleft` when the deque is full casuse an implicit `pop` from the right side to make room for the new element." 520 | ] 521 | } 522 | ], 523 | "metadata": { 524 | "kernelspec": { 525 | "display_name": "Python 3", 526 | "language": "python", 527 | "name": "python3" 528 | }, 529 | "language_info": { 530 | "codemirror_mode": { 531 | "name": "ipython", 532 | "version": 3 533 | }, 534 | "file_extension": ".py", 535 | "mimetype": "text/x-python", 536 | "name": "python", 537 | "nbconvert_exporter": "python", 538 | "pygments_lexer": "ipython3", 539 | "version": "3.7.3" 540 | } 541 | }, 542 | "nbformat": 4, 543 | "nbformat_minor": 2 544 | } 545 | -------------------------------------------------------------------------------- /Contents/Chapter_09.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Chapter 09: Priority Queues" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "## 9.1 Priority Queue Abstract Data Type" 15 | ] 16 | }, 17 | { 18 | "cell_type": "markdown", 19 | "metadata": {}, 20 | "source": [ 21 | "### 9.1.1 Priorities" 22 | ] 23 | }, 24 | { 25 | "cell_type": "markdown", 26 | "metadata": {}, 27 | "source": [ 28 | "In previous section, queue is defined as a collection of object, follows **first-in, first-out (FIFO)** principle. Howver, in practice, we might want to make adjustments in order as necessary. **Priority queue** is a collection of prioritized elements that allow arbitrary element insertion, and allows the removal of the element that has first priority. When an element is added to a priority queue, the user designates its priority by providing an associated **key**. The element with the *minimum* keywill be the next to be removed from the queue." 29 | ] 30 | }, 31 | { 32 | "cell_type": "markdown", 33 | "metadata": {}, 34 | "source": [ 35 | "### 9.1.2 The Priority Queue ADT" 36 | ] 37 | }, 38 | { 39 | "cell_type": "markdown", 40 | "metadata": {}, 41 | "source": [ 42 | "Formally, we model an element and its priority as a key-value pari. Priority queue is notead as `P`." 43 | ] 44 | }, 45 | { 46 | "cell_type": "markdown", 47 | "metadata": {}, 48 | "source": [ 49 | "* `P.add(k, v)`: Insert an item with key `k` and value `v` into priority queue `P`.\n", 50 | "* `P.min()`: Return a tuple, `(k, v)`, representing the key and value of an item in priority queue `P` with minimum key (but do not remove the item); an error occurs if the priority queue is empty.\n", 51 | "* `P.remove_min()`: Remove an item with minimum key from priority queue `P`, and **return a tuple**, `(k, v)`, representing the key and value of the removed item; an error occurs if the priority queue is empty.\n", 52 | "* `P.is_empty()`: Return `True` if priority queue `P` does not contain any items.\n", 53 | "* `len(P)`: Return the number of items in priority queue `P`." 54 | ] 55 | }, 56 | { 57 | "cell_type": "markdown", 58 | "metadata": {}, 59 | "source": [ 60 | "## 9.2 Implementing a Priority Queue" 61 | ] 62 | }, 63 | { 64 | "cell_type": "markdown", 65 | "metadata": {}, 66 | "source": [ 67 | "### 9.2.1 The Composition Design Pattern" 68 | ] 69 | }, 70 | { 71 | "cell_type": "markdown", 72 | "metadata": {}, 73 | "source": [ 74 | "We introdue the **composition design pattern**, defining an `_Item` class that assured that each element reamined paired with its associated count in our primary data structure." 75 | ] 76 | }, 77 | { 78 | "cell_type": "code", 79 | "execution_count": 1, 80 | "metadata": {}, 81 | "outputs": [], 82 | "source": [ 83 | "class PriorityQueueBase:\n", 84 | " \"\"\"Abstract base class for a priority queue.\"\"\"\n", 85 | " \n", 86 | " class _Item:\n", 87 | " \"\"\"Lightweight composite to store priority queue items.\"\"\"\n", 88 | " __slots__ = '_key', '_value'\n", 89 | " \n", 90 | " def __init__(self, k, v):\n", 91 | " self._key = k\n", 92 | " self._value = v\n", 93 | " \n", 94 | " def __lt__(self, other):\n", 95 | " return self._key < other._key\n", 96 | " \n", 97 | " def is_empty(self):\n", 98 | " \"\"\"Return True if the priority queue is empty.\"\"\"\n", 99 | " return len(self) == 0" 100 | ] 101 | }, 102 | { 103 | "source": [ 104 | "### 9.2.2 Implementation with an Unsorted List" 105 | ], 106 | "cell_type": "markdown", 107 | "metadata": {} 108 | }, 109 | { 110 | "source": [ 111 | "First implementation employs `UnsortedPriorityQueue`, inheriting from the `PriorityQueueBase`." 112 | ], 113 | "cell_type": "markdown", 114 | "metadata": {} 115 | }, 116 | { 117 | "source": [ 118 | "Summary of the running times for the `UnsortedPriorityQueue`:\n", 119 | "\n", 120 | "|Operation|Running Time|\n", 121 | "|:---:|:---:|\n", 122 | "|`len`|$O(1)$|\n", 123 | "|`is_empty`|$O(1)$|\n", 124 | "|`add`|$O(1)$|\n", 125 | "|`min`|$O(n)$|\n", 126 | "|`remove_min`|$O(n)$|" 127 | ], 128 | "cell_type": "markdown", 129 | "metadata": {} 130 | }, 131 | { 132 | "cell_type": "code", 133 | "execution_count": 6, 134 | "metadata": {}, 135 | "outputs": [], 136 | "source": [ 137 | "class _DoublyLinkedBase:\n", 138 | " \"\"\"A base calss providing a doubly linked list representation.\"\"\"\n", 139 | " \n", 140 | " class _Node:\n", 141 | " __slots__ = '_element', '_prev', '_next'\n", 142 | " \n", 143 | " def __init__(self, element, prev, nxt):\n", 144 | " self._element = element\n", 145 | " self._prev = prev\n", 146 | " self._next = nxt\n", 147 | " \n", 148 | " def __init__(self):\n", 149 | " self._header = self._Node(None, None, None)\n", 150 | " self._trailer = self._Node(None, None, None)\n", 151 | " self._header._next = self._trailer\n", 152 | " self._trailer._prev = self._header\n", 153 | " self._size = 0\n", 154 | " \n", 155 | " def __len__(self):\n", 156 | " return self._size\n", 157 | " \n", 158 | " def is_empty(self):\n", 159 | " return self._size == 0\n", 160 | " \n", 161 | " def _insert_between(self, e, predecessor, successor):\n", 162 | " newest = self._Node(e, predecessor, successor)\n", 163 | " predecessor._next = newest\n", 164 | " successor._prev = newest\n", 165 | " self._size += 1\n", 166 | " return newest\n", 167 | " \n", 168 | " def _delete_node(self, node):\n", 169 | " predecessor = node._prev\n", 170 | " successor = node._next\n", 171 | " predecessor._next = successor\n", 172 | " successor._prev = predecessor\n", 173 | " self._size -= 1\n", 174 | " element = node._element\n", 175 | " node._prev = node._next = node._element = None\n", 176 | " return element" 177 | ] 178 | }, 179 | { 180 | "cell_type": "code", 181 | "execution_count": 7, 182 | "metadata": {}, 183 | "outputs": [], 184 | "source": [ 185 | "class PositionalList(_DoublyLinkedBase):\n", 186 | " \n", 187 | " class Position:\n", 188 | " \"\"\"An abstraction representing the location of a single element.\"\"\"\n", 189 | " \n", 190 | " def __init__(self, container, node):\n", 191 | " self._container = container\n", 192 | " self._node = node\n", 193 | " \n", 194 | " def element(self):\n", 195 | " return self._node._element\n", 196 | " \n", 197 | " def __eq__(self, other):\n", 198 | " return type(other) is type(self) and other._Node is self._node\n", 199 | " \n", 200 | " def __ne__(self, other):\n", 201 | " return not (self == other)\n", 202 | " \n", 203 | " \n", 204 | " def _validate(self, p):\n", 205 | " if not isinstance(p, self.Position):\n", 206 | " raise TypeError('p must be proper Position type')\n", 207 | " if p._container is not self:\n", 208 | " raise ValueError('p does not belong to this container')\n", 209 | " if p._node._next is None:\n", 210 | " raise ValueError('p is no longer valid')\n", 211 | " return p._node\n", 212 | " \n", 213 | " \n", 214 | " def _make_position(self, node):\n", 215 | " if node is self._header or node is self._trailer:\n", 216 | " return None\n", 217 | " else:\n", 218 | " return self.Position(self, node)\n", 219 | " \n", 220 | " def first(self):\n", 221 | " return self._make_position(self._header._next)\n", 222 | " \n", 223 | " def last(self):\n", 224 | " return self._make_position(self._trailer._prev)\n", 225 | " \n", 226 | " def before(self, p):\n", 227 | " node = self._validate(p)\n", 228 | " return self._make_position(node._prev)\n", 229 | " \n", 230 | " def after(self, p):\n", 231 | " node = self._validate(p)\n", 232 | " return self._make_position(node._next)\n", 233 | " \n", 234 | " def __iter__(self):\n", 235 | " cursor = self.first()\n", 236 | " while cursor is not None:\n", 237 | " yield cursor.element()\n", 238 | " cursor = self.after(cursor)\n", 239 | " \n", 240 | " def _insert_between(self, e, predecessor, successor):\n", 241 | " node = super()._insert_between(e, predecessor, successor)\n", 242 | " return self._make_position(node)\n", 243 | " \n", 244 | " def add_first(self, e):\n", 245 | " return self._insert_between(e, self._header, self.header._next)\n", 246 | " \n", 247 | " def add_last(self, e):\n", 248 | " return self._insert_between(e, self._trailer._prev, self._trailer)\n", 249 | " \n", 250 | " def add_before(self, p, e):\n", 251 | " original = self._validate(p)\n", 252 | " return self._insert_between(e, original._prev, original)\n", 253 | " \n", 254 | " def add_after(self, p, e):\n", 255 | " original = self._validate(p)\n", 256 | " return self._insert_between(e, original, original._next)\n", 257 | " \n", 258 | " def delete(self, p):\n", 259 | " original = self._validate(p)\n", 260 | " return self._delete_node(original)\n", 261 | " \n", 262 | " def replace(self, p, e):\n", 263 | " original = self._validate(p)\n", 264 | " old_value = original._element\n", 265 | " original._element = e\n", 266 | " return old_value\n", 267 | " " 268 | ] 269 | }, 270 | { 271 | "cell_type": "code", 272 | "execution_count": 8, 273 | "metadata": {}, 274 | "outputs": [], 275 | "source": [ 276 | "class UnsortedPriorityQueue(PriorityQueueBase): # base class defines _Item\n", 277 | " \"\"\"A min-oriented priority queue implemented with an unsorted list.\"\"\"\n", 278 | "\n", 279 | " def _find_min(self): # nonpublic utility\n", 280 | " \"\"\"Return Position of item with minimum key.\"\"\"\n", 281 | " if self.is_empty():\n", 282 | " raise Empty(\"Priority queue is empty\")\n", 283 | " small = self._data.first()\n", 284 | " walk = self._data.after(small)\n", 285 | " while walk is not None:\n", 286 | " if walk.element() < small.element():\n", 287 | " small = walk\n", 288 | " walk = self._data.after(walk)\n", 289 | " return small\n", 290 | "\n", 291 | " def __init__(self):\n", 292 | " \"\"\"Create a new empty Priority Queue\"\"\"\n", 293 | " self._data = PositionalList()\n", 294 | " \n", 295 | " def __len__(self):\n", 296 | " \"\"\"Return the number of items in the priority queue.\"\"\"\n", 297 | " return len(self._data)\n", 298 | " \n", 299 | " def add(self, key, value):\n", 300 | " \"\"\"Add a key-value pair.\"\"\"\n", 301 | " self._data.add_last(self._Item(key, value))\n", 302 | "\n", 303 | " def min(self):\n", 304 | " \"\"\"Return but do not remove (k,v) tuple with minimum key.\"\"\"\n", 305 | " p = self._find_min()\n", 306 | " item = p.element()\n", 307 | " return (item._key, item._value)\n", 308 | "\n", 309 | " def remove_min(self):\n", 310 | " \"\"\"Remove and return (k,v) tuple with minimum key.\"\"\"\n", 311 | " p = self._find_min()\n", 312 | " item = self._data.delete(p)\n", 313 | " return (item._key, item._value)\n" 314 | ] 315 | }, 316 | { 317 | "source": [ 318 | "### 9.2.3 Implementation with a Sorted List" 319 | ], 320 | "cell_type": "markdown", 321 | "metadata": {} 322 | }, 323 | { 324 | "source": [ 325 | "An alternative implementation of a priority queue uses a positional list, yet maintaining entries sorted by nondecreasing keys. This ensures that the first element of the list is an entry with the smallest key.\n", 326 | "\n", 327 | "This benefit comes at a cost, for method `add` now requires that we scan the list to find the appropriate position to insert the new item." 328 | ], 329 | "cell_type": "markdown", 330 | "metadata": {} 331 | }, 332 | { 333 | "source": [ 334 | "Worst-case running times of the methods of a priority queue of size $n$. (Implemented by a doubly linked list)\n", 335 | "\n", 336 | "|Operation|Unsorted List|Sorted List|\n", 337 | "|:---:|:---:|:---:|\n", 338 | "|`len`|$O(1)$|$O(1)$|\n", 339 | "|`is_empty`|$O(1)$|$O(1)$|\n", 340 | "|`add`|$O(1)$|$O(n)$|\n", 341 | "|`min`|$O(n)$|$O(1)$|\n", 342 | "|`remove_min`|$O(n)$|$O(1)$|\n" 343 | ], 344 | "cell_type": "markdown", 345 | "metadata": {} 346 | }, 347 | { 348 | "cell_type": "code", 349 | "execution_count": 10, 350 | "metadata": {}, 351 | "outputs": [], 352 | "source": [ 353 | "class SortedPriorityQueue(PriorityQueueBase): # base class defines _Item\n", 354 | " \"\"\"A min-oriented priority queue implemented with a sorted list.\"\"\"\n", 355 | "\n", 356 | " def __init__(self):\n", 357 | " \"\"\"Create a new empty Priority Queue.\"\"\"\n", 358 | " self._data = PositionalList()\n", 359 | "\n", 360 | " def __len__(self):\n", 361 | " \"\"\"Return the number of items in the priority queue.\"\"\"\n", 362 | " return len(self._data)\n", 363 | "\n", 364 | " def add(self, key, value):\n", 365 | " \"\"\"Add a key-value pair.\"\"\"\n", 366 | " newest = self._Item(key, value)\n", 367 | " walk = self._data.last() # walk backward looking for smaller key\n", 368 | " while walk is not None and newest < walk.element():\n", 369 | " walk = self._data.before(walk)\n", 370 | " if walk is None:\n", 371 | " self._data.add_first(newest) # new key is smallest\n", 372 | " else:\n", 373 | " self._data.add_after(walk, newest) # newest goes after walk\n", 374 | "\n", 375 | " def min(self):\n", 376 | " \"\"\"Return but do not remove (k,v) tuple with minimum key.\"\"\"\n", 377 | " if self.is_empty():\n", 378 | " raise Empty(\"Priority queue is empty\")\n", 379 | " p = self._data.first()\n", 380 | " item = p.element()\n", 381 | " return (item._key, item._value)\n", 382 | " \n", 383 | " def remove_min(self):\n", 384 | " \"\"\"Remove and return (k,v) tuple with minimum key.\"\"\"\n", 385 | " if self.is_empty():\n", 386 | " raise Empty(\"Priority queue is empty\")\n", 387 | " item = self._data.delete(self._data.first())\n", 388 | " return (item._key, item._value)" 389 | ] 390 | }, 391 | { 392 | "source": [ 393 | "## 9.3 Heaps" 394 | ], 395 | "cell_type": "markdown", 396 | "metadata": {} 397 | }, 398 | { 399 | "source": [ 400 | "Binary heap is a data structure which can make a priority queue more efficient. This performas both insertions and removals in logarithmic time by using a binary tree. This tree provides reasonable compromise between entirely sorted and unsorted." 401 | ], 402 | "cell_type": "markdown", 403 | "metadata": {} 404 | }, 405 | { 406 | "source": [ 407 | "### 9.3.1 The Heap Data Structure" 408 | ], 409 | "cell_type": "markdown", 410 | "metadata": {} 411 | }, 412 | { 413 | "source": [ 414 | "A heap is a binary tree $T$ that stores a collection of items at its positions and that satisfies **two additional properties**: *a relational property* and *a structural property*" 415 | ], 416 | "cell_type": "markdown", 417 | "metadata": {} 418 | }, 419 | { 420 | "source": [ 421 | "#### Heap-Order Property\n", 422 | "\n", 423 | "In a heap $T$, for every position $p$ other than the root, the key stored at $p$ is greater than or equal to the key stored at $p$'s parent." 424 | ], 425 | "cell_type": "markdown", 426 | "metadata": {} 427 | }, 428 | { 429 | "source": [ 430 | "This guarantees that the root of a tree to have the minimum number in elements." 431 | ], 432 | "cell_type": "markdown", 433 | "metadata": {} 434 | }, 435 | { 436 | "source": [ 437 | "#### Complete Binary Tree Property\n", 438 | "A heap $T$ with height $h$ is a complete binary tree if levels $0, 1, 2, \\ldots, h-1$ of $T$ have the maximum number of nodes possible (namely, level $i$ has $2^{i}$ nodes, for $0 \\leq i \\leq h-1$) and the remaining nodes at level $h$ reside in the left most possible positoins at that level." 439 | ], 440 | "cell_type": "markdown", 441 | "metadata": {} 442 | }, 443 | { 444 | "source": [ 445 | "The properties above make a heap to have tree structure like below:\n", 446 | "\n", 447 | "" 448 | ], 449 | "cell_type": "markdown", 450 | "metadata": {} 451 | }, 452 | { 453 | "source": [ 454 | "#### The Height of a Heap" 455 | ], 456 | "cell_type": "markdown", 457 | "metadata": {} 458 | }, 459 | { 460 | "source": [ 461 | "Let $h$ denote the height of $T$. Insisting that $T$ be complete also has an important consequence, as shown below:\n", 462 | "\n", 463 | "**Proposition 9.2:** A heap $T$ storing $n$ entries has height $h= \\lfloor \\log n \\rfloor$" 464 | ], 465 | "cell_type": "markdown", 466 | "metadata": {} 467 | }, 468 | { 469 | "cell_type": "code", 470 | "execution_count": null, 471 | "metadata": {}, 472 | "outputs": [], 473 | "source": [] 474 | } 475 | ], 476 | "metadata": { 477 | "kernelspec": { 478 | "display_name": "Python 3", 479 | "language": "python", 480 | "name": "python3" 481 | }, 482 | "language_info": { 483 | "codemirror_mode": { 484 | "name": "ipython", 485 | "version": 3 486 | }, 487 | "file_extension": ".py", 488 | "mimetype": "text/x-python", 489 | "name": "python", 490 | "nbconvert_exporter": "python", 491 | "pygments_lexer": "ipython3", 492 | "version": "3.8.5-final" 493 | } 494 | }, 495 | "nbformat": 4, 496 | "nbformat_minor": 4 497 | } -------------------------------------------------------------------------------- /Exercises/Chapter_02_P-2.36.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ 4 | Created on Wed May 24 10:34:00 2017 5 | 6 | @author: Jihoon_Kim 7 | @contact: jioon_kim@outlook.com 8 | """ 9 | import numpy as np 10 | 11 | """ 12 | P-2.36 13 | Write a Python program to simulate an ecosystem containing two types of creatures, bears and fish. The ecosystem consists of a river, which is modeled as a relatively large list. Each element of the list should be a Bear object, a Fish object, or None. In each time step, bsed on a random process, each animal either attempts to move into an adjacent list location or stay where it is. If two animals of the same type are about to collide in the same cell, then they stay where they are, but they create a new instance of that type of animal, which is placed in a random empty (i.e., previously None) location in the list. If a bear and a fish collide, however, then the fish died (i.e., it disappears). 14 | """ 15 | 16 | 17 | class River(object): 18 | def __init__(self, n_room=10, n_animal=8): 19 | self._n_room = n_room 20 | self._eco = [] 21 | self._n_bear = np.random.randint(0, n_animal) 22 | self._n_fish = n_animal - self._n_bear 23 | for i in range(self._n_bear): 24 | self._eco.append("B") # Bear 25 | for i in range(self._n_fish): 26 | self._eco.append("F") # Fish 27 | for i in range(n_room - n_animal): 28 | self._eco.append("N") # None 29 | np.random.shuffle(self._eco) 30 | 31 | def get_eco(self): 32 | print("Eco Status: ", self._eco) 33 | print("Number of Bears: ", self._n_bear) 34 | print("Number of Fishes: ", self._n_fish) 35 | 36 | def add_bear(self, n): 37 | if self._eco[n] == "B": 38 | print("Rejected: Already Occupied.") 39 | elif self._eco[n] == "N": 40 | self._eco[n] = "B" 41 | self._n_bear += 1 42 | else: 43 | print("Bear eats Fish!") 44 | self._eco[n] = "B" 45 | self._n_bear += 1 46 | self._n_fish -= 1 47 | 48 | def add_fish(self, n): 49 | if self._eco[n] == "F": 50 | print("Rejected: Already occupied by another fish.") 51 | elif self._eco[n] == "N": 52 | self._eco[n] = "F" 53 | self._n_fish += 1 54 | else: 55 | print("Rejected: Already occupied by a bear.") 56 | 57 | def kill(self, n): 58 | if self._eco[n] == "B": 59 | self._eco[n] == "N" 60 | self._n_bear -= 1 61 | elif self._eco[n] == "F": 62 | self._eco[n] == "N" 63 | self._n_fish -= 1 64 | else: 65 | print("Already Empty") 66 | -------------------------------------------------------------------------------- /Exercises/Chapter_04.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "source": "# Chapter 04: Recursion", 6 | "metadata": { 7 | "pycharm": { 8 | "metadata": false 9 | } 10 | } 11 | }, 12 | { 13 | "cell_type": "markdown", 14 | "source": "## Modules", 15 | "metadata": { 16 | "pycharm": { 17 | "metadata": false 18 | } 19 | } 20 | }, 21 | { 22 | "cell_type": "code", 23 | "execution_count": 40, 24 | "outputs": [], 25 | "source": "import numpy as np\nimport matplotlib.pyplot as plt\n%matplotlib inline", 26 | "metadata": { 27 | "pycharm": { 28 | "metadata": false, 29 | "name": "#%%\n", 30 | "is_executing": false 31 | } 32 | } 33 | }, 34 | { 35 | "cell_type": "markdown", 36 | "source": "## Exercises\n### R-4.1\nDescribe a recursive algorithm for finding the maximum element in a sequence $S$, of $n$ elements. What is your running time and space usage?", 37 | "metadata": { 38 | "pycharm": { 39 | "metadata": false 40 | } 41 | } 42 | }, 43 | { 44 | "cell_type": "markdown", 45 | "source": "#### Solution", 46 | "metadata": { 47 | "pycharm": { 48 | "metadata": false, 49 | "name": "#%% md\n" 50 | } 51 | } 52 | }, 53 | { 54 | "cell_type": "code", 55 | "execution_count": 52, 56 | "outputs": [], 57 | "source": "def max_recursive(seq, start):\n \n if len(seq) \u003d\u003d 1:\n return seq[0]\n\n if seq[start] \u003c\u003d seq[start + 1]:\n del seq[start]\n \n elif seq[start] \u003e seq[start + 1]:\n del seq[start + 1]\n else:\n return KeyError()\n \n return max_recursive(seq, start)\n\n ", 58 | "metadata": { 59 | "pycharm": { 60 | "metadata": false, 61 | "name": "#%%\n", 62 | "is_executing": false 63 | } 64 | } 65 | }, 66 | { 67 | "cell_type": "code", 68 | "execution_count": 53, 69 | "outputs": [ 70 | { 71 | "name": "stdout", 72 | "text": [ 73 | "maximum of seq1 should be 8\nmaximum of seq1 should be 99\nmaximum of seq1 should be 999\nseq1: 8\nseq2: 99\nseq3: 999\n" 74 | ], 75 | "output_type": "stream" 76 | } 77 | ], 78 | "source": "seq1 \u003d np.random.randint(0, 10, size\u003d(10,)).tolist()\nprint(\"maximum of seq1 should be \", max(seq1))\nseq2 \u003d np.random.randint(0, 100, size\u003d(100,)).tolist()\nprint(\"maximum of seq1 should be \", max(seq2))\nseq3 \u003d np.random.randint(0, 1000, size\u003d(1000,)).tolist()\nprint(\"maximum of seq1 should be \", max(seq3))\n\nprint(\"seq1: \", max_recursive(seq1, 0))\nprint(\"seq2: \", max_recursive(seq2, 0))\nprint(\"seq3: \", max_recursive(seq3, 0))\n", 79 | "metadata": { 80 | "pycharm": { 81 | "metadata": false, 82 | "name": "#%%\n", 83 | "is_executing": false 84 | } 85 | } 86 | }, 87 | { 88 | "cell_type": "markdown", 89 | "source": "### R-4.6\nDescribe a recursive function for computing the $n^\\text{th}$ **Harmonic number** , $H_n \u003d \\sum_{i\u003d1}^n 1/i$", 90 | "metadata": { 91 | "pycharm": { 92 | "metadata": false, 93 | "name": "#%% md\n" 94 | } 95 | } 96 | }, 97 | { 98 | "cell_type": "markdown", 99 | "source": "#### Solution", 100 | "metadata": { 101 | "pycharm": { 102 | "metadata": false, 103 | "name": "#%% md\n" 104 | } 105 | } 106 | }, 107 | { 108 | "cell_type": "code", 109 | "execution_count": 68, 110 | "outputs": [], 111 | "source": "def get_harmonic(n):\n if n \u003d\u003d 1:\n return 1\n return (1 / n) + get_harmonic(n-1)", 112 | "metadata": { 113 | "pycharm": { 114 | "metadata": false, 115 | "name": "#%% \n", 116 | "is_executing": false 117 | } 118 | } 119 | }, 120 | { 121 | "cell_type": "code", 122 | "execution_count": 72, 123 | "outputs": [ 124 | { 125 | "data": { 126 | "text/plain": "\u003cFigure size 432x288 with 1 Axes\u003e", 127 | "image/png": "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\u003d\u003d\n" 128 | }, 129 | "metadata": { 130 | "needs_background": "light" 131 | }, 132 | "output_type": "display_data" 133 | } 134 | ], 135 | "source": "harmonic_series \u003d [get_harmonic(i) for i in range(1, 100)]\nplt.plot(harmonic_series)\nplt.grid()\n", 136 | "metadata": { 137 | "pycharm": { 138 | "metadata": false, 139 | "name": "#%%\n", 140 | "is_executing": false 141 | } 142 | } 143 | }, 144 | { 145 | "cell_type": "markdown", 146 | "metadata": { 147 | "pycharm": {} 148 | }, 149 | "source": "### C-4.14\nIn the ***Towers of Hanoi*** puzzle, we are given a platform with three pegs, $a, b,$ and $c$, \nsticking out of it. On peg $a$ is a stack of $n$ disks, each larger than the next, so that the smallest is on the top and the largest is on the bottom. The puzzle is to move all the disk from peg $a$ to peg $c$, moving one disk at a time, so that we never place a larger disk on top of a smaller one. Describe a recursive algorithm for solving the Towers of Hanoi puzzle for arbitrary $n$." 150 | }, 151 | { 152 | "cell_type": "markdown", 153 | "source": "#### Solution", 154 | "metadata": { 155 | "pycharm": { 156 | "metadata": false, 157 | "name": "#%% md\n" 158 | } 159 | } 160 | }, 161 | { 162 | "cell_type": "code", 163 | "execution_count": 76, 164 | "outputs": [], 165 | "source": "def move_disk(from_peg, to_peg):\n to_peg.append(from_peg.pop())\n print(\"\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d[Status]\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\")\n print(\"[a]: \", a)\n print(\"[b]: \", b)\n print(\"[c]: \", c)\n\ndef hanoi(n, from_peg, help_peg, to_peg):\n if n \u003d\u003d 1:\n move_disk(from_peg, to_peg)\n else:\n hanoi(n-1, from_peg, to_peg, help_peg)\n move_disk(from_peg, to_peg)\n hanoi(n-1, help_peg, from_peg, to_peg)\n", 166 | "metadata": { 167 | "pycharm": { 168 | "metadata": false, 169 | "name": "#%%\n", 170 | "is_executing": false 171 | } 172 | } 173 | }, 174 | { 175 | "cell_type": "code", 176 | "execution_count": 77, 177 | "metadata": { 178 | "pycharm": { 179 | "is_executing": false 180 | } 181 | }, 182 | "outputs": [ 183 | { 184 | "name": "stdout", 185 | "text": [ 186 | "[a]: [4, 3, 2, 1]\n[b]: []\n[c]: []\n" 187 | ], 188 | "output_type": "stream" 189 | } 190 | ], 191 | "source": "n \u003d 4\na \u003d list(reversed(range(1,int(n)+1)))\nb \u003d [];\nc \u003d [];\n\nprint(\"[a]: \", a)\nprint(\"[b]: \", b)\nprint(\"[c]: \", c)" 192 | }, 193 | { 194 | "cell_type": "code", 195 | "execution_count": 78, 196 | "metadata": { 197 | "scrolled": false, 198 | "pycharm": { 199 | "is_executing": false 200 | } 201 | }, 202 | "outputs": [ 203 | { 204 | "name": "stdout", 205 | "text": [ 206 | "\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d[Status]\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\n[a]: [4, 3, 2]\n[b]: [1]\n[c]: []\n\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d[Status]\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\n[a]: [4, 3]\n[b]: [1]\n[c]: [2]\n\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d[Status]\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\n[a]: [4, 3]\n[b]: []\n[c]: [2, 1]\n\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d[Status]\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\n[a]: [4]\n[b]: [3]\n[c]: [2, 1]\n\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d[Status]\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\n[a]: [4, 1]\n[b]: [3]\n[c]: [2]\n\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d[Status]\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\n[a]: [4, 1]\n[b]: [3, 2]\n[c]: []\n\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d[Status]\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\n[a]: [4]\n[b]: [3, 2, 1]\n[c]: []\n\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d[Status]\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\n[a]: []\n[b]: [3, 2, 1]\n[c]: [4]\n\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d[Status]\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\n[a]: []\n[b]: [3, 2]\n[c]: [4, 1]\n\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d[Status]\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\n[a]: [2]\n[b]: [3]\n[c]: [4, 1]\n\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d[Status]\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\n[a]: [2, 1]\n[b]: [3]\n[c]: [4]\n\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d[Status]\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\n[a]: [2, 1]\n[b]: []\n[c]: [4, 3]\n\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d[Status]\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\n[a]: [2]\n[b]: [1]\n[c]: [4, 3]\n\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d[Status]\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\n[a]: []\n[b]: [1]\n[c]: [4, 3, 2]\n\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d[Status]\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\n[a]: []\n[b]: []\n[c]: [4, 3, 2, 1]\n" 207 | ], 208 | "output_type": "stream" 209 | } 210 | ], 211 | "source": "hanoi(4, a, b, c)" 212 | }, 213 | { 214 | "cell_type": "markdown", 215 | "metadata": { 216 | "pycharm": {} 217 | }, 218 | "source": "### C-4.18\nUse recursion to write a Python function for determining if a string $s$ has more vowels than consonants." 219 | }, 220 | { 221 | "cell_type": "markdown", 222 | "source": "#### Solution", 223 | "metadata": { 224 | "pycharm": { 225 | "metadata": false, 226 | "name": "#%% md\n" 227 | } 228 | } 229 | }, 230 | { 231 | "cell_type": "code", 232 | "execution_count": 206, 233 | "outputs": [ 234 | { 235 | "data": { 236 | "text/plain": "[\u0027a\u0027, \u0027e\u0027, \u0027i\u0027, \u0027o\u0027, \u0027u\u0027]" 237 | }, 238 | "metadata": {}, 239 | "output_type": "execute_result", 240 | "execution_count": 206 241 | } 242 | ], 243 | "source": "VOWEL \u003d [\u0027a\u0027,\u0027e\u0027,\u0027i\u0027,\u0027o\u0027,\u0027u\u0027]\nVOWEL", 244 | "metadata": { 245 | "pycharm": { 246 | "metadata": false, 247 | "name": "#%%\n", 248 | "is_executing": false 249 | } 250 | } 251 | }, 252 | { 253 | "cell_type": "code", 254 | "execution_count": 277, 255 | "metadata": { 256 | "pycharm": { 257 | "is_executing": false, 258 | "name": "#%%\n" 259 | } 260 | }, 261 | "outputs": [], 262 | "source": "def more_vowels(s, vowels, consonants):\n if not s:\n vowel_count \u003d len(vowels)\n consonant_count \u003d len(consonants)\n return True if vowel_count \u003e consonant_count else False\n \n if s[-1] in VOWEL:\n vowels.append(s.pop())\n return more_vowels(s, vowels, consonants)\n else:\n consonants.append(s.pop())\n return more_vowels(s, vowels, consonants)\n" 263 | }, 264 | { 265 | "cell_type": "code", 266 | "execution_count": 292, 267 | "metadata": { 268 | "pycharm": { 269 | "is_executing": false 270 | } 271 | }, 272 | "outputs": [ 273 | { 274 | "name": "stdout", 275 | "text": [ 276 | "False\n[\u0027e\u0027, \u0027a\u0027]\n[\u0027l\u0027, \u0027p\u0027, \u0027p\u0027]\n" 277 | ], 278 | "output_type": "stream" 279 | } 280 | ], 281 | "source": "string1_vowels \u003d []\nstring1_consonants \u003d []\nstring1 \u003d list(\u0027apple\u0027)\nprint(more_vowels(string1, string1_vowels, string1_consonants))\nprint(string1_vowels)\nprint(string1_consonants)" 282 | }, 283 | { 284 | "cell_type": "code", 285 | "execution_count": 293, 286 | "outputs": [ 287 | { 288 | "name": "stdout", 289 | "text": [ 290 | "True\n[\u0027o\u0027, \u0027e\u0027, \u0027a\u0027]\n[\u0027r\u0027]\n" 291 | ], 292 | "output_type": "stream" 293 | } 294 | ], 295 | "source": "string2_vowels \u003d []\nstring2_consonants \u003d []\nstring2 \u003d list(\u0027aero\u0027)\nprint(more_vowels(string2, string2_vowels, string2_consonants))\nprint(string2_vowels)\nprint(string2_consonants)\n", 296 | "metadata": { 297 | "pycharm": { 298 | "metadata": false, 299 | "name": "#%%\n", 300 | "is_executing": false 301 | } 302 | } 303 | } 304 | ], 305 | "metadata": { 306 | "kernelspec": { 307 | "name": "pycharm-1115615c", 308 | "language": "python", 309 | "display_name": "PyCharm (Data_Structures_and_Algorithms_in_Python)" 310 | }, 311 | "language_info": { 312 | "codemirror_mode": { 313 | "name": "ipython", 314 | "version": 3 315 | }, 316 | "file_extension": ".py", 317 | "mimetype": "text/x-python", 318 | "name": "python", 319 | "nbconvert_exporter": "python", 320 | "pygments_lexer": "ipython3", 321 | "version": "3.6.1" 322 | }, 323 | "stem_cell": { 324 | "cell_type": "raw", 325 | "source": "", 326 | "metadata": { 327 | "pycharm": { 328 | "metadata": false 329 | } 330 | } 331 | } 332 | }, 333 | "nbformat": 4, 334 | "nbformat_minor": 2 335 | } -------------------------------------------------------------------------------- /Exercises/Chapter_06.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": { 6 | "collapsed": true 7 | }, 8 | "source": [ 9 | "# Chapter 06" 10 | ] 11 | }, 12 | { 13 | "cell_type": "markdown", 14 | "metadata": {}, 15 | "source": [ 16 | "## Modules" 17 | ] 18 | }, 19 | { 20 | "cell_type": "markdown", 21 | "metadata": {}, 22 | "source": [ 23 | "## Exercises" 24 | ] 25 | }, 26 | { 27 | "cell_type": "markdown", 28 | "metadata": {}, 29 | "source": [ 30 | "### R-6.1\n", 31 | "\n", 32 | "What values are returned during the following series of stack operations, if executed upon an initially empty stack?\n", 33 | "\n", 34 | "```\n", 35 | "push(5), push(3), pop(), push(2), push(8), pop(), pop(), push(9), push(1), pop(), push(7), push(6), pop(), pop(), push(4), pop(), pop()\n", 36 | "```" 37 | ] 38 | }, 39 | { 40 | "cell_type": "code", 41 | "execution_count": 1, 42 | "metadata": {}, 43 | "outputs": [], 44 | "source": [ 45 | "class Empty(Exception):\n", 46 | " pass" 47 | ] 48 | }, 49 | { 50 | "cell_type": "code", 51 | "execution_count": 2, 52 | "metadata": {}, 53 | "outputs": [], 54 | "source": [ 55 | "class ArrayStack:\n", 56 | " \"\"\"LIFO Stack implementatino using a Python list as underlying storage.\"\"\"\n", 57 | " \n", 58 | " def __init__(self):\n", 59 | " self._data = []\n", 60 | " \n", 61 | " def __len__(self):\n", 62 | " return len(self._data)\n", 63 | " \n", 64 | " def is_empty(self):\n", 65 | " return len(self._data) == 0\n", 66 | " \n", 67 | " def push(self, e):\n", 68 | " self._data.append(e)\n", 69 | " \n", 70 | " def top(self):\n", 71 | " if self.is_empty():\n", 72 | " raise Empty('Stack is empty')\n", 73 | " return self._data[-1]\n", 74 | " \n", 75 | " def pop(self):\n", 76 | " if self.is_empty():\n", 77 | " raise Empty('Stack is empty')\n", 78 | " return self._data.pop()" 79 | ] 80 | }, 81 | { 82 | "cell_type": "code", 83 | "execution_count": 3, 84 | "metadata": {}, 85 | "outputs": [], 86 | "source": [ 87 | "x = ArrayStack()" 88 | ] 89 | }, 90 | { 91 | "cell_type": "code", 92 | "execution_count": 4, 93 | "metadata": {}, 94 | "outputs": [ 95 | { 96 | "data": { 97 | "text/plain": [ 98 | "9" 99 | ] 100 | }, 101 | "execution_count": 4, 102 | "metadata": {}, 103 | "output_type": "execute_result" 104 | } 105 | ], 106 | "source": [ 107 | "x.push(5)\n", 108 | "x.push(3)\n", 109 | "x.pop()\n", 110 | "x.push(2)\n", 111 | "x.push(8)\n", 112 | "x.pop()\n", 113 | "x.pop()\n", 114 | "x.push(9)\n", 115 | "x.push(1)\n", 116 | "x.pop()\n", 117 | "x.push(7)\n", 118 | "x.push(6)\n", 119 | "x.pop()\n", 120 | "x.pop()\n", 121 | "x.push(4)\n", 122 | "x.pop()\n", 123 | "x.pop()" 124 | ] 125 | }, 126 | { 127 | "cell_type": "markdown", 128 | "metadata": {}, 129 | "source": [ 130 | "### R-6.2\n", 131 | "\n", 132 | "Suppose initially empty stack `S` has executed a total of 25 `push` operations, 12 `top` operations, and 10 `pop` operations, 3 of which raised `Empty` errors that were caught and ignored. What is the current size of `S`?" 133 | ] 134 | }, 135 | { 136 | "cell_type": "markdown", 137 | "metadata": {}, 138 | "source": [ 139 | "The `top` does not affect the number of remaining elements in a stack. Following case is the one of the possible case when 3 errors occur from the given operations. " 140 | ] 141 | }, 142 | { 143 | "cell_type": "code", 144 | "execution_count": 5, 145 | "metadata": {}, 146 | "outputs": [], 147 | "source": [ 148 | "x = ArrayStack()" 149 | ] 150 | }, 151 | { 152 | "cell_type": "code", 153 | "execution_count": 6, 154 | "metadata": {}, 155 | "outputs": [], 156 | "source": [ 157 | "class ArrayStack:\n", 158 | " \"\"\"LIFO Stack implementatino using a Python list as underlying storage.\"\"\"\n", 159 | " \n", 160 | " def __init__(self):\n", 161 | " self._data = []\n", 162 | " \n", 163 | " def __len__(self):\n", 164 | " return len(self._data)\n", 165 | " \n", 166 | " def is_empty(self):\n", 167 | " return len(self._data) == 0\n", 168 | " \n", 169 | " def push(self, e):\n", 170 | " self._data.append(e)\n", 171 | " \n", 172 | " def top(self):\n", 173 | " if self.is_empty():\n", 174 | " raise Empty('Stack is empty')\n", 175 | " return self._data[-1]\n", 176 | " \n", 177 | " def pop(self):\n", 178 | " if self.is_empty():\n", 179 | " print(\"EMPTY WARNING\")\n", 180 | " return\n", 181 | "# raise Empty('Stack is empty')\n", 182 | " return self._data.pop()" 183 | ] 184 | }, 185 | { 186 | "cell_type": "code", 187 | "execution_count": 7, 188 | "metadata": {}, 189 | "outputs": [ 190 | { 191 | "ename": "Empty", 192 | "evalue": "Stack is empty", 193 | "output_type": "error", 194 | "traceback": [ 195 | "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", 196 | "\u001b[0;31mEmpty\u001b[0m Traceback (most recent call last)", 197 | "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 2\u001b[0m \u001b[0mx\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mpush\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mi\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 3\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mi\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mrange\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m10\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 4\u001b[0;31m \u001b[0mx\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mpop\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 5\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mi\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mrange\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m18\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 6\u001b[0m \u001b[0mx\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mpush\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mi\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", 198 | "\u001b[0;32m\u001b[0m in \u001b[0;36mpop\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 21\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mpop\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 22\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mis_empty\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 23\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mEmpty\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'Stack is empty'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 24\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_data\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mpop\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", 199 | "\u001b[0;31mEmpty\u001b[0m: Stack is empty" 200 | ] 201 | } 202 | ], 203 | "source": [ 204 | "for i in range(7):\n", 205 | " x.push(i)\n", 206 | "for i in range(10):\n", 207 | " x.pop()\n", 208 | "for i in range(18):\n", 209 | " x.push(i)" 210 | ] 211 | }, 212 | { 213 | "cell_type": "markdown", 214 | "metadata": {}, 215 | "source": [ 216 | "We can think 3 of 10 `pop` couldn't effectively pop elements because it was empty." 217 | ] 218 | }, 219 | { 220 | "cell_type": "markdown", 221 | "metadata": {}, 222 | "source": [ 223 | "$$ 25 - (10 - 3) = 18$$" 224 | ] 225 | }, 226 | { 227 | "cell_type": "markdown", 228 | "metadata": {}, 229 | "source": [ 230 | "### R-6.3\n", 231 | "\n", 232 | "Implement a function with signature `transfer(S, T)` that transfers all elements from stack `S` onto stack `T`, so that the element that starts at the top of `S` is the first to be inserted onto `T`, and the element at the bottom of `S` ends up at the top of `T`." 233 | ] 234 | }, 235 | { 236 | "cell_type": "code", 237 | "execution_count": 8, 238 | "metadata": {}, 239 | "outputs": [], 240 | "source": [ 241 | "class ArrayStack:\n", 242 | " \"\"\"LIFO Stack implementatino using a Python list as underlying storage.\"\"\"\n", 243 | " \n", 244 | " def __init__(self):\n", 245 | " self._data = []\n", 246 | " \n", 247 | " @property\n", 248 | " def data(self):\n", 249 | " return self._data\n", 250 | " \n", 251 | " def __len__(self):\n", 252 | " return len(self._data)\n", 253 | " \n", 254 | " def is_empty(self):\n", 255 | " return len(self._data) == 0\n", 256 | " \n", 257 | " def push(self, e):\n", 258 | " self._data.append(e)\n", 259 | " \n", 260 | " def top(self):\n", 261 | " if self.is_empty():\n", 262 | " raise Empty('Stack is empty')\n", 263 | " return self._data[-1]\n", 264 | " \n", 265 | " def pop(self):\n", 266 | " if self.is_empty():\n", 267 | " raise Empty('Stack is empty')\n", 268 | " return self._data.pop()\n", 269 | " \n", 270 | " def transfer(self, S: ArrayStack) -> None:\n", 271 | " stck_len = len(self._data)\n", 272 | " [S.push(self.pop()) for _ in range(stck_len)]\n", 273 | " " 274 | ] 275 | }, 276 | { 277 | "cell_type": "code", 278 | "execution_count": 9, 279 | "metadata": {}, 280 | "outputs": [], 281 | "source": [ 282 | "x = ArrayStack()\n", 283 | "for i in range(10):\n", 284 | " x.push(i)" 285 | ] 286 | }, 287 | { 288 | "cell_type": "code", 289 | "execution_count": 10, 290 | "metadata": {}, 291 | "outputs": [ 292 | { 293 | "data": { 294 | "text/plain": [ 295 | "[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]" 296 | ] 297 | }, 298 | "execution_count": 10, 299 | "metadata": {}, 300 | "output_type": "execute_result" 301 | } 302 | ], 303 | "source": [ 304 | "x.data" 305 | ] 306 | }, 307 | { 308 | "cell_type": "code", 309 | "execution_count": 11, 310 | "metadata": {}, 311 | "outputs": [], 312 | "source": [ 313 | "y = ArrayStack()" 314 | ] 315 | }, 316 | { 317 | "cell_type": "code", 318 | "execution_count": 12, 319 | "metadata": {}, 320 | "outputs": [], 321 | "source": [ 322 | "x.transfer(y)" 323 | ] 324 | }, 325 | { 326 | "cell_type": "code", 327 | "execution_count": 13, 328 | "metadata": {}, 329 | "outputs": [ 330 | { 331 | "data": { 332 | "text/plain": [ 333 | "[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]" 334 | ] 335 | }, 336 | "execution_count": 13, 337 | "metadata": {}, 338 | "output_type": "execute_result" 339 | } 340 | ], 341 | "source": [ 342 | "y.data" 343 | ] 344 | }, 345 | { 346 | "cell_type": "markdown", 347 | "metadata": {}, 348 | "source": [ 349 | "### R-6.4\n", 350 | "\n", 351 | "Give a recursive method for removing all the elements from a stack." 352 | ] 353 | }, 354 | { 355 | "cell_type": "code", 356 | "execution_count": 14, 357 | "metadata": {}, 358 | "outputs": [], 359 | "source": [ 360 | "class ArrayStack:\n", 361 | " \"\"\"LIFO Stack implementatino using a Python list as underlying storage.\"\"\"\n", 362 | " \n", 363 | " def __init__(self):\n", 364 | " self._data = []\n", 365 | " \n", 366 | " @property\n", 367 | " def data(self):\n", 368 | " return self._data\n", 369 | " \n", 370 | " def __len__(self):\n", 371 | " return len(self._data)\n", 372 | " \n", 373 | " def is_empty(self):\n", 374 | " return len(self._data) == 0\n", 375 | " \n", 376 | " def push(self, e):\n", 377 | " self._data.append(e)\n", 378 | " \n", 379 | " def top(self):\n", 380 | " if self.is_empty():\n", 381 | " raise Empty('Stack is empty')\n", 382 | " return self._data[-1]\n", 383 | " \n", 384 | " def pop(self):\n", 385 | " if self.is_empty():\n", 386 | " raise Empty('Stack is empty')\n", 387 | " return self._data.pop()\n", 388 | " \n", 389 | " def transfer(self, S: ArrayStack) -> None:\n", 390 | " stck_len = len(self._data)\n", 391 | " (S.push(self.pop()) for _ in range(stck_len))\n", 392 | " \n", 393 | " def pop_all(self):\n", 394 | " if self.data:\n", 395 | " self.pop()\n", 396 | " return self.pop_all()" 397 | ] 398 | }, 399 | { 400 | "cell_type": "code", 401 | "execution_count": 15, 402 | "metadata": {}, 403 | "outputs": [], 404 | "source": [ 405 | "x = ArrayStack()" 406 | ] 407 | }, 408 | { 409 | "cell_type": "code", 410 | "execution_count": 16, 411 | "metadata": {}, 412 | "outputs": [], 413 | "source": [ 414 | "for i in range(10):\n", 415 | " x.push(i)" 416 | ] 417 | }, 418 | { 419 | "cell_type": "code", 420 | "execution_count": 17, 421 | "metadata": {}, 422 | "outputs": [ 423 | { 424 | "data": { 425 | "text/plain": [ 426 | "[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]" 427 | ] 428 | }, 429 | "execution_count": 17, 430 | "metadata": {}, 431 | "output_type": "execute_result" 432 | } 433 | ], 434 | "source": [ 435 | "x.data" 436 | ] 437 | }, 438 | { 439 | "cell_type": "code", 440 | "execution_count": 18, 441 | "metadata": {}, 442 | "outputs": [], 443 | "source": [ 444 | "x.pop_all()" 445 | ] 446 | }, 447 | { 448 | "cell_type": "code", 449 | "execution_count": 19, 450 | "metadata": {}, 451 | "outputs": [ 452 | { 453 | "data": { 454 | "text/plain": [ 455 | "[]" 456 | ] 457 | }, 458 | "execution_count": 19, 459 | "metadata": {}, 460 | "output_type": "execute_result" 461 | } 462 | ], 463 | "source": [ 464 | "x.data" 465 | ] 466 | }, 467 | { 468 | "cell_type": "markdown", 469 | "metadata": {}, 470 | "source": [ 471 | "### R-6.7\n", 472 | "\n", 473 | "What values are returned during the following sequence of queue operations, if executed on an initially empty queue?\n", 474 | "\n", 475 | "```\n", 476 | "enqueue(5), enqueue(3), dequeue(), enqueue(2), enqueue(8), dequeue(), dequeue(), enqueue(9), enqueue(1), dequeue(), enqueue(7), enqueue(6), dequeue(), dequeue(), enqueue(4), dequeue(), dequeue()\n", 477 | "```" 478 | ] 479 | }, 480 | { 481 | "cell_type": "code", 482 | "execution_count": 20, 483 | "metadata": {}, 484 | "outputs": [], 485 | "source": [ 486 | "class ArrayQueue:\n", 487 | " \n", 488 | " DEFAULT_CAPACITY = 10\n", 489 | " \n", 490 | " def __init__(self):\n", 491 | " self._data = [None] * ArrayQueue.DEFAULT_CAPACITY\n", 492 | " self._size = 0\n", 493 | " self._front = 0\n", 494 | " \n", 495 | " def __len__(self):\n", 496 | " return self._size\n", 497 | " \n", 498 | " def is_empty(self):\n", 499 | " return self._size == 0\n", 500 | " \n", 501 | " def first(self):\n", 502 | " if self.is_empty():\n", 503 | " raise Empty(\"Queue is empty\")\n", 504 | " return self._data[self._front]\n", 505 | " \n", 506 | " def dequeue(self):\n", 507 | " \n", 508 | " if self.is_empty():\n", 509 | " raise Empty(\"Queue is empty\")\n", 510 | " answer = self._data[self._front]\n", 511 | " self._data[self._front] = None\n", 512 | " self._front = (self._front +1) % len(self._data)\n", 513 | " self._size -= 1\n", 514 | " \n", 515 | " if 0 < self._size < len(self._data) // 4:\n", 516 | " self._resize(len(self._data) // 2)\n", 517 | " return answer\n", 518 | " \n", 519 | " def enqueue(self, e):\n", 520 | " if self._size == len(self._data):\n", 521 | " self._resize(2 * len(self._data))\n", 522 | " avail = (self._front + self._size) % len(self._data)\n", 523 | " self._data[avail] = e\n", 524 | " self._size += 1\n", 525 | " \n", 526 | " def _resize(self, cap):\n", 527 | " \n", 528 | " old = self._data\n", 529 | " self._data = [None] * cap\n", 530 | " walk = self._front\n", 531 | " for k in range(self._size):\n", 532 | " self._data[k] = old[walk]\n", 533 | " walk = (1 + walk) % len(old)\n", 534 | " self._front = 0" 535 | ] 536 | }, 537 | { 538 | "cell_type": "code", 539 | "execution_count": 21, 540 | "metadata": {}, 541 | "outputs": [], 542 | "source": [ 543 | "x = ArrayQueue()" 544 | ] 545 | }, 546 | { 547 | "cell_type": "code", 548 | "execution_count": 22, 549 | "metadata": {}, 550 | "outputs": [ 551 | { 552 | "data": { 553 | "text/plain": [ 554 | "6" 555 | ] 556 | }, 557 | "execution_count": 22, 558 | "metadata": {}, 559 | "output_type": "execute_result" 560 | } 561 | ], 562 | "source": [ 563 | "x.enqueue(5)\n", 564 | "x.enqueue(3)\n", 565 | "x.dequeue()\n", 566 | "x.enqueue(2)\n", 567 | "x.enqueue(8)\n", 568 | "x.dequeue()\n", 569 | "x.dequeue()\n", 570 | "x.enqueue(9)\n", 571 | "x.enqueue(1)\n", 572 | "x.dequeue()\n", 573 | "x.enqueue(7)\n", 574 | "x.enqueue(6)\n", 575 | "x.dequeue()\n", 576 | "x.dequeue()\n", 577 | "x.enqueue(4)\n", 578 | "x.dequeue()\n", 579 | "x.dequeue()" 580 | ] 581 | }, 582 | { 583 | "cell_type": "markdown", 584 | "metadata": {}, 585 | "source": [ 586 | "### R-6.8\n", 587 | "\n", 588 | "Suppose an initially empty queue `Q` has executed a total of 32 `enqueue` operations, 10 `first` operations, and 15 `dequeue` operations, 5 of which raised `Empty` errors that were caught and ignored. What is the current size of `Q`." 589 | ] 590 | }, 591 | { 592 | "cell_type": "markdown", 593 | "metadata": {}, 594 | "source": [ 595 | "$$ 32 - (15 - 5) = 22 $$" 596 | ] 597 | }, 598 | { 599 | "cell_type": "markdown", 600 | "metadata": {}, 601 | "source": [ 602 | "### C-6.16\n", 603 | "\n", 604 | "Modify the ArrayStack implementation so that the stack’s capacity is limited to `maxlen` elements, where `maxlen` is an optional parameter to the constructor (that defaults to None). If `push` is called when the stack is at full capacity, throw a `Full` exception (defined similarly to Empty)." 605 | ] 606 | }, 607 | { 608 | "cell_type": "code", 609 | "execution_count": 23, 610 | "metadata": {}, 611 | "outputs": [], 612 | "source": [ 613 | "# Stack\n", 614 | "\n", 615 | "class Empty(Exception):\n", 616 | " \"\"\"Error attempting to access an element from an empty container.\"\"\"\n", 617 | " pass\n", 618 | "\n", 619 | "class Full(Exception):\n", 620 | " \"\"\"Error when stack is full\"\"\"\n", 621 | " pass\n", 622 | "\n", 623 | "class ArrayStack:\n", 624 | " \"\"\"LIFO Stack implementation using a Python list as underlying storage.\"\"\"\n", 625 | "\n", 626 | " def __init__(self, maxlen=None):\n", 627 | " \"\"\"Create an empty stack.\"\"\"\n", 628 | " self._data = []\n", 629 | " self._maxlen = maxlen\n", 630 | "\n", 631 | " def __len__(self):\n", 632 | " \"\"\"Return the number of elements in the stack\"\"\"\n", 633 | " return len(self._data)\n", 634 | "\n", 635 | " def is_empty(self):\n", 636 | " \"\"\"Return True if the stack is empty.\"\"\"\n", 637 | " return len(self._data) == 0\n", 638 | "\n", 639 | " def push(self, e):\n", 640 | " \"\"\"Add element e to the top of the stack.\"\"\"\n", 641 | " \n", 642 | " if self._maxlen is not None and (len(self._data) >= self._maxlen):\n", 643 | " raise Full('Stack is full.')\n", 644 | " self._data.append(e)\n", 645 | "\n", 646 | " def top(self):\n", 647 | " \"\"\"Return (but do not remove) the element at the top of the stack.\n", 648 | "\n", 649 | " Raise Empty exception if the stack is empty.\n", 650 | " \"\"\"\n", 651 | "\n", 652 | " if self.is_empty():\n", 653 | " raise Empty('Stack is empty')\n", 654 | " return self._data[-1]\n", 655 | "\n", 656 | " def pop(self):\n", 657 | " \"\"\"Remove and return the element from the top of the stack (i.e.,LIFO)\n", 658 | "\n", 659 | " Raise Empty exception if the stack is empty.\n", 660 | " \"\"\"\n", 661 | " if self.is_empty():\n", 662 | " raise Empty('Stack is empty')\n", 663 | " return self._data.pop()" 664 | ] 665 | }, 666 | { 667 | "cell_type": "code", 668 | "execution_count": 24, 669 | "metadata": {}, 670 | "outputs": [], 671 | "source": [ 672 | "sample = ArrayStack(5)" 673 | ] 674 | }, 675 | { 676 | "cell_type": "code", 677 | "execution_count": 25, 678 | "metadata": {}, 679 | "outputs": [], 680 | "source": [ 681 | "sample.push(2)\n", 682 | "sample.push(3)\n", 683 | "sample.push(4)\n", 684 | "sample.push(5)\n", 685 | "sample.push(6)" 686 | ] 687 | }, 688 | { 689 | "cell_type": "code", 690 | "execution_count": 26, 691 | "metadata": {}, 692 | "outputs": [ 693 | { 694 | "ename": "Full", 695 | "evalue": "Stack is full.", 696 | "output_type": "error", 697 | "traceback": [ 698 | "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", 699 | "\u001b[0;31mFull\u001b[0m Traceback (most recent call last)", 700 | "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0msample\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mpush\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m7\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", 701 | "\u001b[0;32m\u001b[0m in \u001b[0;36mpush\u001b[0;34m(self, e)\u001b[0m\n\u001b[1;32m 29\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 30\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_maxlen\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0;32mNone\u001b[0m \u001b[0;32mand\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_data\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m>=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_maxlen\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 31\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mFull\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'Stack is full.'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 32\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_data\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mappend\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0me\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 33\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", 702 | "\u001b[0;31mFull\u001b[0m: Stack is full." 703 | ] 704 | } 705 | ], 706 | "source": [ 707 | "sample.push(7)" 708 | ] 709 | }, 710 | { 711 | "cell_type": "code", 712 | "execution_count": 27, 713 | "metadata": {}, 714 | "outputs": [], 715 | "source": [ 716 | "sample_none = ArrayStack()" 717 | ] 718 | }, 719 | { 720 | "cell_type": "code", 721 | "execution_count": 28, 722 | "metadata": {}, 723 | "outputs": [], 724 | "source": [ 725 | "sample_none.push(2)\n", 726 | "sample_none.push(3)\n", 727 | "sample_none.push(4)\n", 728 | "sample_none.push(5)\n", 729 | "sample_none.push(6)" 730 | ] 731 | }, 732 | { 733 | "cell_type": "code", 734 | "execution_count": 29, 735 | "metadata": {}, 736 | "outputs": [], 737 | "source": [ 738 | "sample_none.push(7)" 739 | ] 740 | }, 741 | { 742 | "cell_type": "markdown", 743 | "metadata": {}, 744 | "source": [ 745 | "### C-6.17\n", 746 | "\n", 747 | "In the previous exercise, we assume that the underlying list is initially empty. Redo that exercise, this time preallocating an underlying list with length equal to the stack's maximum capacity." 748 | ] 749 | }, 750 | { 751 | "cell_type": "code", 752 | "execution_count": 36, 753 | "metadata": {}, 754 | "outputs": [], 755 | "source": [ 756 | "# Stack\n", 757 | "\n", 758 | "class Empty(Exception):\n", 759 | " \"\"\"Error attempting to access an element from an empty container.\"\"\"\n", 760 | " pass\n", 761 | "\n", 762 | "class Full(Exception):\n", 763 | " \"\"\"Error when stack is full\"\"\"\n", 764 | " pass\n", 765 | "\n", 766 | "class ArrayStack:\n", 767 | " \"\"\"LIFO Stack implementation using a Python list as underlying storage.\"\"\"\n", 768 | "\n", 769 | " def __init__(self, maxlen=None):\n", 770 | " \"\"\"Create an empty stack.\"\"\"\n", 771 | " self._data = [] if maxlen is None else [None] * maxlen\n", 772 | " self._maxlen = maxlen\n", 773 | "\n", 774 | " def __len__(self):\n", 775 | " \"\"\"Return the number of elements in the stack\"\"\"\n", 776 | " return len(self._data)\n", 777 | "\n", 778 | " @property\n", 779 | " def data(self):\n", 780 | " return self._data\n", 781 | " \n", 782 | " def is_empty(self):\n", 783 | " \"\"\"Return True if the stack is empty.\"\"\"\n", 784 | " return len(self._data) == 0\n", 785 | "\n", 786 | " def push(self, e):\n", 787 | " \"\"\"Add element e to the top of the stack.\"\"\"\n", 788 | " \n", 789 | " if self._maxlen is not None and (len(self._data) >= self._maxlen):\n", 790 | " raise Full('Stack is full.')\n", 791 | " self._data.append(e)\n", 792 | "\n", 793 | " def top(self):\n", 794 | " \"\"\"Return (but do not remove) the element at the top of the stack.\n", 795 | "\n", 796 | " Raise Empty exception if the stack is empty.\n", 797 | " \"\"\"\n", 798 | "\n", 799 | " if self.is_empty():\n", 800 | " raise Empty('Stack is empty')\n", 801 | " return self._data[-1]\n", 802 | "\n", 803 | " def pop(self):\n", 804 | " \"\"\"Remove and return the element from the top of the stack (i.e.,LIFO)\n", 805 | "\n", 806 | " Raise Empty exception if the stack is empty.\n", 807 | " \"\"\"\n", 808 | " if self.is_empty():\n", 809 | " raise Empty('Stack is empty')\n", 810 | " return self._data.pop()" 811 | ] 812 | }, 813 | { 814 | "cell_type": "code", 815 | "execution_count": 31, 816 | "metadata": {}, 817 | "outputs": [], 818 | "source": [ 819 | "x = ArrayStack()" 820 | ] 821 | }, 822 | { 823 | "cell_type": "code", 824 | "execution_count": 32, 825 | "metadata": {}, 826 | "outputs": [ 827 | { 828 | "data": { 829 | "text/plain": [ 830 | "[]" 831 | ] 832 | }, 833 | "execution_count": 32, 834 | "metadata": {}, 835 | "output_type": "execute_result" 836 | } 837 | ], 838 | "source": [ 839 | "x._data" 840 | ] 841 | }, 842 | { 843 | "cell_type": "code", 844 | "execution_count": 33, 845 | "metadata": {}, 846 | "outputs": [ 847 | { 848 | "data": { 849 | "text/plain": [ 850 | "[None, None, None, None, None, None, None, None, None, None]" 851 | ] 852 | }, 853 | "execution_count": 33, 854 | "metadata": {}, 855 | "output_type": "execute_result" 856 | } 857 | ], 858 | "source": [ 859 | "x = ArrayStack(10)\n", 860 | "x._data" 861 | ] 862 | }, 863 | { 864 | "cell_type": "markdown", 865 | "metadata": {}, 866 | "source": [ 867 | "### C-6.23\n", 868 | "\n", 869 | "Suppose you have three nonempty stacks $R$, $S$, and $T$. Describe a sequence of operations that results in $S$ storing all elements originally in $T$ below all of $S$'s original elements, with both sets of those elements in their original order. The final configuration for $R$ should be the smae as its original configuration. For example, if $R = [1, 2, 3]$, $S=[4,5]$, and $T=[6,7,8,9]$, the final configuration should have $R = [1,2,3]$ and $S=[6,7,8,9,4,5]$." 870 | ] 871 | }, 872 | { 873 | "cell_type": "code", 874 | "execution_count": 50, 875 | "metadata": { 876 | "scrolled": false 877 | }, 878 | "outputs": [ 879 | { 880 | "data": { 881 | "text/plain": [ 882 | "[1, 2, 3]" 883 | ] 884 | }, 885 | "execution_count": 50, 886 | "metadata": {}, 887 | "output_type": "execute_result" 888 | } 889 | ], 890 | "source": [ 891 | "r = ArrayStack()\n", 892 | "r.push(1)\n", 893 | "r.push(2)\n", 894 | "r.push(3)\n", 895 | "r.data" 896 | ] 897 | }, 898 | { 899 | "cell_type": "code", 900 | "execution_count": 51, 901 | "metadata": {}, 902 | "outputs": [ 903 | { 904 | "data": { 905 | "text/plain": [ 906 | "[4, 5]" 907 | ] 908 | }, 909 | "execution_count": 51, 910 | "metadata": {}, 911 | "output_type": "execute_result" 912 | } 913 | ], 914 | "source": [ 915 | "s = ArrayStack()\n", 916 | "s.push(4)\n", 917 | "s.push(5)\n", 918 | "s.data" 919 | ] 920 | }, 921 | { 922 | "cell_type": "code", 923 | "execution_count": 52, 924 | "metadata": {}, 925 | "outputs": [ 926 | { 927 | "data": { 928 | "text/plain": [ 929 | "[6, 7, 8, 9]" 930 | ] 931 | }, 932 | "execution_count": 52, 933 | "metadata": {}, 934 | "output_type": "execute_result" 935 | } 936 | ], 937 | "source": [ 938 | "t = ArrayStack()\n", 939 | "t.push(6)\n", 940 | "t.push(7)\n", 941 | "t.push(8)\n", 942 | "t.push(9)\n", 943 | "t.data" 944 | ] 945 | }, 946 | { 947 | "cell_type": "code", 948 | "execution_count": 53, 949 | "metadata": {}, 950 | "outputs": [], 951 | "source": [ 952 | "r.push(s.pop())\n", 953 | "t.push(s.pop())\n", 954 | "t.push(r.pop())" 955 | ] 956 | }, 957 | { 958 | "cell_type": "code", 959 | "execution_count": 54, 960 | "metadata": {}, 961 | "outputs": [ 962 | { 963 | "data": { 964 | "text/plain": [ 965 | "[1, 2, 3]" 966 | ] 967 | }, 968 | "execution_count": 54, 969 | "metadata": {}, 970 | "output_type": "execute_result" 971 | } 972 | ], 973 | "source": [ 974 | "r.data" 975 | ] 976 | }, 977 | { 978 | "cell_type": "code", 979 | "execution_count": 55, 980 | "metadata": {}, 981 | "outputs": [ 982 | { 983 | "data": { 984 | "text/plain": [ 985 | "[]" 986 | ] 987 | }, 988 | "execution_count": 55, 989 | "metadata": {}, 990 | "output_type": "execute_result" 991 | } 992 | ], 993 | "source": [ 994 | "s.data" 995 | ] 996 | }, 997 | { 998 | "cell_type": "code", 999 | "execution_count": 56, 1000 | "metadata": {}, 1001 | "outputs": [ 1002 | { 1003 | "data": { 1004 | "text/plain": [ 1005 | "[6, 7, 8, 9, 4, 5]" 1006 | ] 1007 | }, 1008 | "execution_count": 56, 1009 | "metadata": {}, 1010 | "output_type": "execute_result" 1011 | } 1012 | ], 1013 | "source": [ 1014 | "t.data" 1015 | ] 1016 | }, 1017 | { 1018 | "cell_type": "markdown", 1019 | "metadata": {}, 1020 | "source": [ 1021 | "### C-6.30\n", 1022 | "\n", 1023 | "Alice has two queues, $Q$ and $R$, which can store integers, Bob gives Alice 50 odd integers and 50 even integers and insists that she store all 100 integers in $Q$ and $R$. They then paly a game where Bob picks $Q$ or $R$ at random and then applies the round-robin scheduler, described in the chapter, to the chosen queue a random number of times. If the last number to be processed at the end of this game was odd, Bob wins. Otherwise Alice wins. How can Alice allocate integers to queues to optimize her change of winnning? What is her chance of winning?" 1024 | ] 1025 | }, 1026 | { 1027 | "cell_type": "markdown", 1028 | "metadata": {}, 1029 | "source": [ 1030 | "#### Answer" 1031 | ] 1032 | }, 1033 | { 1034 | "cell_type": "markdown", 1035 | "metadata": {}, 1036 | "source": [ 1037 | "For me, it is not very intuitive to guess the most probable case that Alice wins, but it is helpful to think two most extreme cases to infer the answer of this question." 1038 | ] 1039 | }, 1040 | { 1041 | "cell_type": "markdown", 1042 | "metadata": {}, 1043 | "source": [ 1044 | "##### Case 1: Distribute almost equally." 1045 | ] 1046 | }, 1047 | { 1048 | "cell_type": "markdown", 1049 | "metadata": {}, 1050 | "source": [ 1051 | "If two queues have equal number of even and odd integers, then probability of Alice can win will be:" 1052 | ] 1053 | }, 1054 | { 1055 | "cell_type": "markdown", 1056 | "metadata": {}, 1057 | "source": [ 1058 | "$$0.5 \\times \\left(\\frac{25}{50} + \\frac{25}{50}\\right) = 50(\\%) $$" 1059 | ] 1060 | }, 1061 | { 1062 | "cell_type": "markdown", 1063 | "metadata": {}, 1064 | "source": [ 1065 | "##### Case 2: Allocate an even number to a queue and others at the other queue." 1066 | ] 1067 | }, 1068 | { 1069 | "cell_type": "markdown", 1070 | "metadata": {}, 1071 | "source": [ 1072 | "This will lead the probablility that Alice wins to:\n", 1073 | "\n", 1074 | "$$0.5 \\times \\left(1 + \\frac{49}{99}\\right) \\approx 74.75(\\%)$$" 1075 | ] 1076 | }, 1077 | { 1078 | "cell_type": "markdown", 1079 | "metadata": {}, 1080 | "source": [ 1081 | "Looks good. Then How about allocating more than one even integers to a queue? Let's test with 25 even integers." 1082 | ] 1083 | }, 1084 | { 1085 | "cell_type": "markdown", 1086 | "metadata": {}, 1087 | "source": [ 1088 | "$$0.5 \\times \\left(1 + \\frac{25}{75}\\right) \\approx 66.67(\\%)$$" 1089 | ] 1090 | }, 1091 | { 1092 | "cell_type": "markdown", 1093 | "metadata": {}, 1094 | "source": [ 1095 | "You might notice that allocating more than one even integer just reduce the Alice's winning probability of the case that Bob chooses the queue which have both even and odd integers. Therefore, Alice should allocate an even integer to one queue." 1096 | ] 1097 | } 1098 | ], 1099 | "metadata": { 1100 | "kernelspec": { 1101 | "display_name": "Python 3", 1102 | "language": "python", 1103 | "name": "python3" 1104 | }, 1105 | "language_info": { 1106 | "codemirror_mode": { 1107 | "name": "ipython", 1108 | "version": 3 1109 | }, 1110 | "file_extension": ".py", 1111 | "mimetype": "text/x-python", 1112 | "name": "python", 1113 | "nbconvert_exporter": "python", 1114 | "pygments_lexer": "ipython3", 1115 | "version": "3.7.3" 1116 | } 1117 | }, 1118 | "nbformat": 4, 1119 | "nbformat_minor": 1 1120 | } 1121 | -------------------------------------------------------------------------------- /Exercises/Chapter_07.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Chapter 07" 8 | ] 9 | }, 10 | { 11 | "cell_type": "code", 12 | "execution_count": 1, 13 | "metadata": {}, 14 | "outputs": [], 15 | "source": [ 16 | "import copy" 17 | ] 18 | }, 19 | { 20 | "cell_type": "markdown", 21 | "metadata": {}, 22 | "source": [ 23 | "## Exercises" 24 | ] 25 | }, 26 | { 27 | "cell_type": "markdown", 28 | "metadata": {}, 29 | "source": [ 30 | "### R-7.2\n", 31 | "\n", 32 | "Describe a good algorithm for concatenating two singly linked lists L and M, given only reference to the first node of each list, into a single list $L^\\prime$ that contains all the nodes of $L$ followed by all nodes of $M$." 33 | ] 34 | }, 35 | { 36 | "cell_type": "markdown", 37 | "metadata": {}, 38 | "source": [ 39 | "##### Answer" 40 | ] 41 | }, 42 | { 43 | "cell_type": "code", 44 | "execution_count": 2, 45 | "metadata": {}, 46 | "outputs": [], 47 | "source": [ 48 | "class Empty(Exception):\n", 49 | " pass" 50 | ] 51 | }, 52 | { 53 | "cell_type": "code", 54 | "execution_count": 3, 55 | "metadata": {}, 56 | "outputs": [], 57 | "source": [ 58 | "class LinkedStack:\n", 59 | " \"\"\"LIFO Stack implementatino using a singly linked list for storage\"\"\"\n", 60 | " \n", 61 | " class _Node:\n", 62 | " __slots__ = '_element', '_next'\n", 63 | " \n", 64 | " def __init__(self, element, nxt):\n", 65 | " self._element = element\n", 66 | " self._next = nxt\n", 67 | " \n", 68 | " \n", 69 | " def __init__(self):\n", 70 | " self._head = None\n", 71 | " self._size = 0\n", 72 | " \n", 73 | " def __len__(self):\n", 74 | " return self._size\n", 75 | " \n", 76 | " def __iter__(self):\n", 77 | " cur = self._head\n", 78 | " while cur is not None:\n", 79 | " yield cur._element\n", 80 | " cur = cur._next\n", 81 | " \n", 82 | " def is_empty(self):\n", 83 | " return self._size == 0\n", 84 | " \n", 85 | " def push(self, e):\n", 86 | " self._head = self._Node(e, self._head)\n", 87 | " self._size += 1\n", 88 | " \n", 89 | " def top(self):\n", 90 | " if self.is_empty():\n", 91 | " raise Empty('Stack is empty')\n", 92 | " return self._head._element\n", 93 | " \n", 94 | " def pop(self):\n", 95 | " if self.is_empty():\n", 96 | " raise Empty('Stack is empty')\n", 97 | " answer = self._head._element\n", 98 | " self._head = self._head._next\n", 99 | " self._size -= 1\n", 100 | " return answer\n" 101 | ] 102 | }, 103 | { 104 | "cell_type": "code", 105 | "execution_count": 4, 106 | "metadata": {}, 107 | "outputs": [], 108 | "source": [ 109 | "L = LinkedStack()" 110 | ] 111 | }, 112 | { 113 | "cell_type": "code", 114 | "execution_count": 5, 115 | "metadata": {}, 116 | "outputs": [], 117 | "source": [ 118 | "L.push(5)\n", 119 | "L.push(4)\n", 120 | "L.push(3)" 121 | ] 122 | }, 123 | { 124 | "cell_type": "code", 125 | "execution_count": 6, 126 | "metadata": {}, 127 | "outputs": [ 128 | { 129 | "data": { 130 | "text/plain": [ 131 | "[3, 4, 5]" 132 | ] 133 | }, 134 | "execution_count": 6, 135 | "metadata": {}, 136 | "output_type": "execute_result" 137 | } 138 | ], 139 | "source": [ 140 | "[i for i in L]" 141 | ] 142 | }, 143 | { 144 | "cell_type": "code", 145 | "execution_count": 7, 146 | "metadata": {}, 147 | "outputs": [], 148 | "source": [ 149 | "M = LinkedStack()\n", 150 | "M.push(3)\n", 151 | "M.push(2)\n", 152 | "M.push(1)" 153 | ] 154 | }, 155 | { 156 | "cell_type": "code", 157 | "execution_count": 8, 158 | "metadata": {}, 159 | "outputs": [ 160 | { 161 | "data": { 162 | "text/plain": [ 163 | "[1, 2, 3]" 164 | ] 165 | }, 166 | "execution_count": 8, 167 | "metadata": {}, 168 | "output_type": "execute_result" 169 | } 170 | ], 171 | "source": [ 172 | "[i for i in M]" 173 | ] 174 | }, 175 | { 176 | "cell_type": "markdown", 177 | "metadata": {}, 178 | "source": [ 179 | "Making a new `LinkedStack` is less efficient ($n$ pop & $n$ push for each list) than just making the base list's last node to designate the head of the list to be appended ($n$ references for traversing). " 180 | ] 181 | }, 182 | { 183 | "cell_type": "code", 184 | "execution_count": 9, 185 | "metadata": {}, 186 | "outputs": [], 187 | "source": [ 188 | "def concat_singly_linked_stack(base, append):\n", 189 | " \n", 190 | " base = copy.deepcopy(base)\n", 191 | " append = copy.deepcopy(append)\n", 192 | " \n", 193 | " base_last = None\n", 194 | " cur = base._head\n", 195 | " while True: # Need to traverse whole list since LinkedStack does not keep its last node.\n", 196 | " if cur._next is None:\n", 197 | " base_last = cur\n", 198 | " break\n", 199 | " cur = cur._next\n", 200 | " base_last._next = append._head\n", 201 | " base._size += append._size\n", 202 | " return base" 203 | ] 204 | }, 205 | { 206 | "cell_type": "code", 207 | "execution_count": 10, 208 | "metadata": {}, 209 | "outputs": [], 210 | "source": [ 211 | "concat_list = concat_singly_linked_stack(L, M)" 212 | ] 213 | }, 214 | { 215 | "cell_type": "code", 216 | "execution_count": 11, 217 | "metadata": {}, 218 | "outputs": [ 219 | { 220 | "data": { 221 | "text/plain": [ 222 | "[3, 4, 5, 1, 2, 3]" 223 | ] 224 | }, 225 | "execution_count": 11, 226 | "metadata": {}, 227 | "output_type": "execute_result" 228 | } 229 | ], 230 | "source": [ 231 | "[i for i in concat_list]" 232 | ] 233 | }, 234 | { 235 | "cell_type": "markdown", 236 | "metadata": {}, 237 | "source": [ 238 | "### R-7.3\n", 239 | "\n", 240 | "Describe a recursive algorithm that counts the number of nodes in a singly linked list." 241 | ] 242 | }, 243 | { 244 | "cell_type": "markdown", 245 | "metadata": {}, 246 | "source": [ 247 | "##### Answer" 248 | ] 249 | }, 250 | { 251 | "cell_type": "code", 252 | "execution_count": 12, 253 | "metadata": {}, 254 | "outputs": [], 255 | "source": [ 256 | "def recursive_count(list_head):\n", 257 | " \"\"\"\n", 258 | " list_head: head reference to a list\n", 259 | " \"\"\"\n", 260 | " \n", 261 | " if list_head._next is None:\n", 262 | " return 1\n", 263 | " else:\n", 264 | " return 1 + recursive_count(list_head._next)\n" 265 | ] 266 | }, 267 | { 268 | "cell_type": "code", 269 | "execution_count": 13, 270 | "metadata": {}, 271 | "outputs": [ 272 | { 273 | "data": { 274 | "text/plain": [ 275 | "6" 276 | ] 277 | }, 278 | "execution_count": 13, 279 | "metadata": {}, 280 | "output_type": "execute_result" 281 | } 282 | ], 283 | "source": [ 284 | "recursive_count(concat_list._head)" 285 | ] 286 | }, 287 | { 288 | "cell_type": "markdown", 289 | "metadata": {}, 290 | "source": [ 291 | "### R-7.7\n", 292 | "\n", 293 | "Our `CircularQueue` class provides a `rotate()` method that has semantics equivalent to `Q.enqueue(Q.dequeue())`, for a nonempty queue. Implement such a method for the `LinkedQueue ` class without the creation of any new nodes." 294 | ] 295 | }, 296 | { 297 | "cell_type": "markdown", 298 | "metadata": {}, 299 | "source": [ 300 | "##### Answer" 301 | ] 302 | }, 303 | { 304 | "cell_type": "code", 305 | "execution_count": 14, 306 | "metadata": {}, 307 | "outputs": [], 308 | "source": [ 309 | "class LinkedQueue:\n", 310 | " \n", 311 | " class _Node:\n", 312 | " __slots__ = '_element', '_next'\n", 313 | " \n", 314 | " def __init__(self, element, nxt):\n", 315 | " self._element = element\n", 316 | " self._next = nxt\n", 317 | " \n", 318 | " def __init__(self):\n", 319 | " self._head = None\n", 320 | " self._tail = None\n", 321 | " self._size = 0\n", 322 | " \n", 323 | " def __len__(self):\n", 324 | " return self._size\n", 325 | " \n", 326 | " def __iter__(self):\n", 327 | " cur = self._head\n", 328 | " while cur is not None:\n", 329 | " yield cur._element\n", 330 | " cur = cur._next\n", 331 | " \n", 332 | " def is_empty(self):\n", 333 | " return self._size == 0\n", 334 | " \n", 335 | " def first(self):\n", 336 | " if self.is_empty():\n", 337 | " raise Empty(\"Queue is empty\")\n", 338 | " return self._head._element\n", 339 | " \n", 340 | " def dequeue(self):\n", 341 | " \n", 342 | " if self.is_empty():\n", 343 | " raise Empty(\"Queue is empty\")\n", 344 | " answer = self._head._element\n", 345 | " self._head = self._head._next\n", 346 | " self._size -= 1\n", 347 | " if self.is_empty():\n", 348 | " self._tail = None\n", 349 | " return answer\n", 350 | " \n", 351 | " def enqueue(self, e):\n", 352 | " \n", 353 | " newest = self._Node(e, None)\n", 354 | " if self.is_empty():\n", 355 | " self._head = newest\n", 356 | " else:\n", 357 | " self._tail._next = newest\n", 358 | " self._tail = newest\n", 359 | " self._size += 1\n", 360 | " " 361 | ] 362 | }, 363 | { 364 | "cell_type": "markdown", 365 | "metadata": {}, 366 | "source": [ 367 | "`rotate` method in `LinkedQueue` should `dequeue` the first element and insert it back into its tail by `enqueue`" 368 | ] 369 | }, 370 | { 371 | "cell_type": "code", 372 | "execution_count": 15, 373 | "metadata": {}, 374 | "outputs": [], 375 | "source": [ 376 | "def rotate(self):\n", 377 | " if self._size > 0:\n", 378 | " old_head = self._head\n", 379 | " self._head = old_head._next\n", 380 | " self._tail._next = old_head\n", 381 | " old_head._next = None\n", 382 | " \n", 383 | "LinkedQueue.rotate = rotate # monkey patching" 384 | ] 385 | }, 386 | { 387 | "cell_type": "code", 388 | "execution_count": 16, 389 | "metadata": {}, 390 | "outputs": [], 391 | "source": [ 392 | "q = LinkedQueue()\n", 393 | "q.enqueue(5)\n", 394 | "q.enqueue(4)\n", 395 | "q.enqueue(3)\n", 396 | "q.enqueue(2)\n", 397 | "q.enqueue(1)" 398 | ] 399 | }, 400 | { 401 | "cell_type": "code", 402 | "execution_count": 17, 403 | "metadata": {}, 404 | "outputs": [], 405 | "source": [ 406 | "q.rotate()" 407 | ] 408 | }, 409 | { 410 | "cell_type": "code", 411 | "execution_count": 18, 412 | "metadata": {}, 413 | "outputs": [ 414 | { 415 | "data": { 416 | "text/plain": [ 417 | "[4, 3, 2, 1, 5]" 418 | ] 419 | }, 420 | "execution_count": 18, 421 | "metadata": {}, 422 | "output_type": "execute_result" 423 | } 424 | ], 425 | "source": [ 426 | "[i for i in q]" 427 | ] 428 | }, 429 | { 430 | "cell_type": "markdown", 431 | "metadata": {}, 432 | "source": [ 433 | "### R-7.13" 434 | ] 435 | }, 436 | { 437 | "cell_type": "markdown", 438 | "metadata": {}, 439 | "source": [ 440 | "Update the `PositionalList` class to support an additional method `find(e)`, which returns the position of the (first occurrence of )element `e` in the list (or None if not found)." 441 | ] 442 | }, 443 | { 444 | "cell_type": "code", 445 | "execution_count": 19, 446 | "metadata": {}, 447 | "outputs": [], 448 | "source": [ 449 | "class _DoublyLinkedBase:\n", 450 | " \"\"\"A base calss providing a doubly linked list representation.\"\"\"\n", 451 | " \n", 452 | " class _Node:\n", 453 | " __slots__ = '_element', '_prev', '_next'\n", 454 | " \n", 455 | " def __init__(self, element, prev, nxt):\n", 456 | " self._element = element\n", 457 | " self._prev = prev\n", 458 | " self._next = nxt\n", 459 | " \n", 460 | " def __init__(self):\n", 461 | " self._header = self._Node(None, None, None)\n", 462 | " self._trailer = self._Node(None, None, None)\n", 463 | " self._header._next = self._trailer\n", 464 | " self._trailer._prev = self._header\n", 465 | " self._size = 0\n", 466 | " \n", 467 | " def __len__(self):\n", 468 | " return self._size\n", 469 | " \n", 470 | " def is_empty(self):\n", 471 | " return self._size == 0\n", 472 | " \n", 473 | " def _insert_between(self, e, predecessor, successor):\n", 474 | " newest = self._Node(e, predecessor, successor)\n", 475 | " predecessor._next = newest\n", 476 | " successor._prev = newest\n", 477 | " self._size += 1\n", 478 | " return newest\n", 479 | " \n", 480 | " def _delete_node(self, node):\n", 481 | " predecessor = node._prev\n", 482 | " successor = node._next\n", 483 | " predecessor._next = successor\n", 484 | " successor._prev = predecessor\n", 485 | " self._size -= 1\n", 486 | " element = node._element\n", 487 | " node._prev = node._next = node._element = None\n", 488 | " return element" 489 | ] 490 | }, 491 | { 492 | "cell_type": "code", 493 | "execution_count": 20, 494 | "metadata": {}, 495 | "outputs": [], 496 | "source": [ 497 | "class PositionalList(_DoublyLinkedBase):\n", 498 | " \n", 499 | " class Position:\n", 500 | " \"\"\"An abstraction representing the location of a single element.\"\"\"\n", 501 | " \n", 502 | " def __init__(self, container, node):\n", 503 | " self._container = container\n", 504 | " self._node = node\n", 505 | " \n", 506 | " def element(self):\n", 507 | " return self._node._element\n", 508 | " \n", 509 | " def __eq__(self, other):\n", 510 | " return type(other) is type(self) and other._Node is self._node\n", 511 | " \n", 512 | " def __ne__(self, other):\n", 513 | " return not (self == other)\n", 514 | " \n", 515 | " \n", 516 | " def _validate(self, p):\n", 517 | " if not isinstance(p, self.Position):\n", 518 | " raise TypeError('p must be proper Position type')\n", 519 | " if p._container is not self:\n", 520 | " raise ValueError('p does not belong to this container')\n", 521 | " if p._node._next is None:\n", 522 | " raise ValueError('p is no longer valid')\n", 523 | " return p._node\n", 524 | " \n", 525 | " \n", 526 | " def _make_position(self, node):\n", 527 | " if node is self._header or node is self._trailer:\n", 528 | " return None\n", 529 | " else:\n", 530 | " return self.Position(self, node)\n", 531 | " \n", 532 | " def first(self):\n", 533 | " return self._make_position(self._header._next)\n", 534 | " \n", 535 | " def last(self):\n", 536 | " return self._make_position(self._trailer._prev)\n", 537 | " \n", 538 | " def before(self, p):\n", 539 | " node = self._validate(p)\n", 540 | " return self._make_position(node._prev)\n", 541 | " \n", 542 | " def after(self, p):\n", 543 | " node = self._validate(p)\n", 544 | " return self._make_position(node._next)\n", 545 | " \n", 546 | " def __iter__(self):\n", 547 | " cursor = self.first()\n", 548 | " while cursor is not None:\n", 549 | " yield cursor.element()\n", 550 | " cursor = self.after(cursor)\n", 551 | " \n", 552 | " def _insert_between(self, e, predecessor, successor):\n", 553 | " node = super()._insert_between(e, predecessor, successor)\n", 554 | " return self._make_position(node)\n", 555 | " \n", 556 | " def add_first(self, e):\n", 557 | " return self._insert_between(e, self._header, self._header._next)\n", 558 | " \n", 559 | " def add_last(self, e):\n", 560 | " return self._insert_between(e, self._trailer._prev, self._trailer)\n", 561 | " \n", 562 | " def add_before(self, p, e):\n", 563 | " original = self._validate(p)\n", 564 | " return self._insert_between(e, original._prev, original)\n", 565 | " \n", 566 | " def add_after(self, p, e):\n", 567 | " original = self._validate(p)\n", 568 | " return self._insert_between(e, original, original._next)\n", 569 | " \n", 570 | " def delete(self, p):\n", 571 | " original = self._validate(p)\n", 572 | " return self._delete_node(original)\n", 573 | " \n", 574 | " def replace(self, p, e):\n", 575 | " original = self._validate(p)\n", 576 | " old_value = original._element\n", 577 | " original._element = e\n", 578 | " return old_value\n", 579 | " " 580 | ] 581 | }, 582 | { 583 | "cell_type": "code", 584 | "execution_count": 21, 585 | "metadata": {}, 586 | "outputs": [], 587 | "source": [ 588 | "def find(self, e):\n", 589 | " cur = self.first()\n", 590 | " while True:\n", 591 | " if cur is None:\n", 592 | " return None\n", 593 | " if cur.element() == e:\n", 594 | " return cur\n", 595 | " cur = self.after(cur)\n", 596 | "\n", 597 | "PositionalList.find = find # Monkey patching" 598 | ] 599 | }, 600 | { 601 | "cell_type": "code", 602 | "execution_count": 22, 603 | "metadata": {}, 604 | "outputs": [], 605 | "source": [ 606 | "p = PositionalList()" 607 | ] 608 | }, 609 | { 610 | "cell_type": "code", 611 | "execution_count": 23, 612 | "metadata": {}, 613 | "outputs": [], 614 | "source": [ 615 | "for i in [2, 8 ,1, 10, 9, 8, 4, 3]:\n", 616 | " p.add_first(i)" 617 | ] 618 | }, 619 | { 620 | "cell_type": "code", 621 | "execution_count": 24, 622 | "metadata": {}, 623 | "outputs": [ 624 | { 625 | "data": { 626 | "text/plain": [ 627 | "3" 628 | ] 629 | }, 630 | "execution_count": 24, 631 | "metadata": {}, 632 | "output_type": "execute_result" 633 | } 634 | ], 635 | "source": [ 636 | "p.first().element()" 637 | ] 638 | }, 639 | { 640 | "cell_type": "code", 641 | "execution_count": 25, 642 | "metadata": {}, 643 | "outputs": [ 644 | { 645 | "data": { 646 | "text/plain": [ 647 | "[3, 4, 8, 9, 10, 1, 8, 2]" 648 | ] 649 | }, 650 | "execution_count": 25, 651 | "metadata": {}, 652 | "output_type": "execute_result" 653 | } 654 | ], 655 | "source": [ 656 | "[i for i in p]" 657 | ] 658 | }, 659 | { 660 | "cell_type": "code", 661 | "execution_count": 26, 662 | "metadata": {}, 663 | "outputs": [ 664 | { 665 | "data": { 666 | "text/plain": [ 667 | "<__main__.PositionalList.Position at 0x7fc2a05e9be0>" 668 | ] 669 | }, 670 | "execution_count": 26, 671 | "metadata": {}, 672 | "output_type": "execute_result" 673 | } 674 | ], 675 | "source": [ 676 | "p.find(10)" 677 | ] 678 | }, 679 | { 680 | "cell_type": "code", 681 | "execution_count": 27, 682 | "metadata": {}, 683 | "outputs": [ 684 | { 685 | "data": { 686 | "text/plain": [ 687 | "9" 688 | ] 689 | }, 690 | "execution_count": 27, 691 | "metadata": {}, 692 | "output_type": "execute_result" 693 | } 694 | ], 695 | "source": [ 696 | "p.before(p.find(10)).element()" 697 | ] 698 | }, 699 | { 700 | "cell_type": "code", 701 | "execution_count": 28, 702 | "metadata": {}, 703 | "outputs": [ 704 | { 705 | "data": { 706 | "text/plain": [ 707 | "<__main__.PositionalList.Position at 0x7fc2a063aba8>" 708 | ] 709 | }, 710 | "execution_count": 28, 711 | "metadata": {}, 712 | "output_type": "execute_result" 713 | } 714 | ], 715 | "source": [ 716 | "p.find(8)" 717 | ] 718 | }, 719 | { 720 | "cell_type": "code", 721 | "execution_count": 29, 722 | "metadata": {}, 723 | "outputs": [ 724 | { 725 | "data": { 726 | "text/plain": [ 727 | "9" 728 | ] 729 | }, 730 | "execution_count": 29, 731 | "metadata": {}, 732 | "output_type": "execute_result" 733 | } 734 | ], 735 | "source": [ 736 | "p.after(p.find(8)).element()" 737 | ] 738 | }, 739 | { 740 | "cell_type": "code", 741 | "execution_count": 30, 742 | "metadata": {}, 743 | "outputs": [], 744 | "source": [ 745 | "p.find(99) # Not found: None" 746 | ] 747 | }, 748 | { 749 | "cell_type": "markdown", 750 | "metadata": {}, 751 | "source": [ 752 | "### R-7.14\n", 753 | "\n", 754 | "Repeat the previous process using recursion. Your method should not contain any loops. How much space does your method use in addition to the space used for $L$?" 755 | ] 756 | }, 757 | { 758 | "cell_type": "code", 759 | "execution_count": 31, 760 | "metadata": {}, 761 | "outputs": [], 762 | "source": [ 763 | "def find_recursive(self, cur_pos, e):\n", 764 | " \n", 765 | " if cur_pos is None:\n", 766 | " return None\n", 767 | " elif cur_pos.element() == e:\n", 768 | " return cur_pos\n", 769 | " else:\n", 770 | " cur_pos = self.after(cur_pos)\n", 771 | " return find_recursive(self, cur_pos, e)\n", 772 | " \n", 773 | "\n", 774 | "PositionalList.find_recursive = find_recursive # Monkey patching" 775 | ] 776 | }, 777 | { 778 | "cell_type": "code", 779 | "execution_count": 32, 780 | "metadata": {}, 781 | "outputs": [ 782 | { 783 | "data": { 784 | "text/plain": [ 785 | "[3, 4, 8, 9, 10, 1, 8, 2]" 786 | ] 787 | }, 788 | "execution_count": 32, 789 | "metadata": {}, 790 | "output_type": "execute_result" 791 | } 792 | ], 793 | "source": [ 794 | "[i for i in p]" 795 | ] 796 | }, 797 | { 798 | "cell_type": "code", 799 | "execution_count": 33, 800 | "metadata": {}, 801 | "outputs": [ 802 | { 803 | "data": { 804 | "text/plain": [ 805 | "<__main__.PositionalList.Position at 0x7fc2a05e9a20>" 806 | ] 807 | }, 808 | "execution_count": 33, 809 | "metadata": {}, 810 | "output_type": "execute_result" 811 | } 812 | ], 813 | "source": [ 814 | "p.find_recursive(p.first(), 1)" 815 | ] 816 | }, 817 | { 818 | "cell_type": "code", 819 | "execution_count": 34, 820 | "metadata": {}, 821 | "outputs": [ 822 | { 823 | "data": { 824 | "text/plain": [ 825 | "10" 826 | ] 827 | }, 828 | "execution_count": 34, 829 | "metadata": {}, 830 | "output_type": "execute_result" 831 | } 832 | ], 833 | "source": [ 834 | "p.before(p.find_recursive(p.first(), 1)).element()" 835 | ] 836 | }, 837 | { 838 | "cell_type": "code", 839 | "execution_count": 35, 840 | "metadata": {}, 841 | "outputs": [ 842 | { 843 | "data": { 844 | "text/plain": [ 845 | "<__main__.PositionalList.Position at 0x7fc2a0650588>" 846 | ] 847 | }, 848 | "execution_count": 35, 849 | "metadata": {}, 850 | "output_type": "execute_result" 851 | } 852 | ], 853 | "source": [ 854 | "p.find_recursive(p.first(), 8)" 855 | ] 856 | }, 857 | { 858 | "cell_type": "code", 859 | "execution_count": 36, 860 | "metadata": {}, 861 | "outputs": [ 862 | { 863 | "data": { 864 | "text/plain": [ 865 | "9" 866 | ] 867 | }, 868 | "execution_count": 36, 869 | "metadata": {}, 870 | "output_type": "execute_result" 871 | } 872 | ], 873 | "source": [ 874 | "p.after(p.find_recursive(p.first(), 8)).element()" 875 | ] 876 | }, 877 | { 878 | "cell_type": "code", 879 | "execution_count": 37, 880 | "metadata": {}, 881 | "outputs": [], 882 | "source": [ 883 | "p.find_recursive(p.first(), 99) # Not found: None" 884 | ] 885 | }, 886 | { 887 | "cell_type": "markdown", 888 | "metadata": {}, 889 | "source": [ 890 | "### C-7.24\n", 891 | "\n", 892 | "Give a complete implementation of the stack ADT using a singly linked list that *includes a header sentinel*." 893 | ] 894 | }, 895 | { 896 | "cell_type": "code", 897 | "execution_count": 38, 898 | "metadata": {}, 899 | "outputs": [], 900 | "source": [ 901 | "class LinkedStackwithSentinel:\n", 902 | " \"\"\"LIFO Stack implementatino using a singly linked list for storage\"\"\"\n", 903 | " \n", 904 | " class _Node:\n", 905 | " __slots__ = '_element', '_next'\n", 906 | " \n", 907 | " def __init__(self, element, nxt):\n", 908 | " self._element = element\n", 909 | " self._next = nxt\n", 910 | " \n", 911 | " \n", 912 | " def __init__(self):\n", 913 | " self._header = self._Node(None, None)\n", 914 | " self._header._next = None\n", 915 | " self._size = 0\n", 916 | " \n", 917 | " def __len__(self):\n", 918 | " return self._size\n", 919 | " \n", 920 | " def is_empty(self):\n", 921 | " return self._size == 0\n", 922 | " \n", 923 | " def push(self, e):\n", 924 | " new = self._Node(e, self._header._next)\n", 925 | " self._header._next = new\n", 926 | " self._size += 1\n", 927 | " \n", 928 | " def top(self):\n", 929 | " if self.is_empty():\n", 930 | " raise Empty('Stack is empty')\n", 931 | " return self._header._next._element\n", 932 | " \n", 933 | " def pop(self):\n", 934 | " if self.is_empty():\n", 935 | " raise Empty('Stack is empty')\n", 936 | " pop_node = self._header._next\n", 937 | " self._header._next = pop_node._next\n", 938 | " answer = pop_node._element\n", 939 | " pop_node = None\n", 940 | " self._size -= 1\n", 941 | " return answer" 942 | ] 943 | }, 944 | { 945 | "cell_type": "code", 946 | "execution_count": 39, 947 | "metadata": {}, 948 | "outputs": [], 949 | "source": [ 950 | "x = LinkedStackwithSentinel()" 951 | ] 952 | }, 953 | { 954 | "cell_type": "code", 955 | "execution_count": 40, 956 | "metadata": {}, 957 | "outputs": [], 958 | "source": [ 959 | "x.push(5)\n", 960 | "x.push(3)\n", 961 | "x.push(1)" 962 | ] 963 | }, 964 | { 965 | "cell_type": "code", 966 | "execution_count": 41, 967 | "metadata": {}, 968 | "outputs": [ 969 | { 970 | "data": { 971 | "text/plain": [ 972 | "1" 973 | ] 974 | }, 975 | "execution_count": 41, 976 | "metadata": {}, 977 | "output_type": "execute_result" 978 | } 979 | ], 980 | "source": [ 981 | "x.top()" 982 | ] 983 | }, 984 | { 985 | "cell_type": "code", 986 | "execution_count": 42, 987 | "metadata": {}, 988 | "outputs": [ 989 | { 990 | "name": "stdout", 991 | "output_type": "stream", 992 | "text": [ 993 | "1\n", 994 | "3\n", 995 | "5\n" 996 | ] 997 | } 998 | ], 999 | "source": [ 1000 | "print(x.pop())\n", 1001 | "print(x.pop())\n", 1002 | "print(x.pop())" 1003 | ] 1004 | }, 1005 | { 1006 | "cell_type": "code", 1007 | "execution_count": 43, 1008 | "metadata": {}, 1009 | "outputs": [ 1010 | { 1011 | "name": "stdout", 1012 | "output_type": "stream", 1013 | "text": [ 1014 | "proper error\n" 1015 | ] 1016 | } 1017 | ], 1018 | "source": [ 1019 | "try:\n", 1020 | " x.pop()\n", 1021 | " \n", 1022 | "except Empty:\n", 1023 | " print(\"proper error\")" 1024 | ] 1025 | }, 1026 | { 1027 | "cell_type": "markdown", 1028 | "metadata": {}, 1029 | "source": [ 1030 | "### C-7.25\n", 1031 | "\n", 1032 | "Give a complete implementation of the queue ADT using a singly linked list that includes a header sentinel." 1033 | ] 1034 | }, 1035 | { 1036 | "cell_type": "code", 1037 | "execution_count": 44, 1038 | "metadata": {}, 1039 | "outputs": [], 1040 | "source": [ 1041 | "class LinkedQueuewithSentinel:\n", 1042 | " \n", 1043 | " class _Node:\n", 1044 | " __slots__ = '_element', '_next'\n", 1045 | " \n", 1046 | " def __init__(self, element, nxt):\n", 1047 | " self._element = element\n", 1048 | " self._next = nxt\n", 1049 | " \n", 1050 | " def __init__(self):\n", 1051 | " self._header = self._Node(None, None)\n", 1052 | " self._header._next = None\n", 1053 | " self._tail = None\n", 1054 | " self._size = 0\n", 1055 | " \n", 1056 | " def __len__(self):\n", 1057 | " return self._size\n", 1058 | " \n", 1059 | " def is_empty(self):\n", 1060 | " return self._size == 0\n", 1061 | " \n", 1062 | " def first(self):\n", 1063 | " if self.is_empty():\n", 1064 | " raise Empty(\"Queue is empty\")\n", 1065 | " return self._header._next._element\n", 1066 | " \n", 1067 | " def dequeue(self):\n", 1068 | " \n", 1069 | " if self.is_empty():\n", 1070 | " raise Empty(\"Queue is empty\")\n", 1071 | " dequeue_node = self._header._next\n", 1072 | " self._header._next = dequeue_node._next\n", 1073 | " answer = dequeue_node._element\n", 1074 | " dequeue_node = None\n", 1075 | " self._size -= 1\n", 1076 | " if self.is_empty():\n", 1077 | " self._tail = None\n", 1078 | " return answer\n", 1079 | " \n", 1080 | " def enqueue(self, e):\n", 1081 | " \n", 1082 | " newest = self._Node(e, None)\n", 1083 | " if self.is_empty():\n", 1084 | " self._header._next = newest\n", 1085 | " else:\n", 1086 | " self._tail._next = newest\n", 1087 | " self._tail = newest\n", 1088 | " self._size += 1\n", 1089 | " " 1090 | ] 1091 | }, 1092 | { 1093 | "cell_type": "code", 1094 | "execution_count": 45, 1095 | "metadata": {}, 1096 | "outputs": [], 1097 | "source": [ 1098 | "x = LinkedQueuewithSentinel()" 1099 | ] 1100 | }, 1101 | { 1102 | "cell_type": "code", 1103 | "execution_count": 46, 1104 | "metadata": {}, 1105 | "outputs": [], 1106 | "source": [ 1107 | "x.enqueue(5)\n", 1108 | "x.enqueue(4)\n", 1109 | "x.enqueue(3)" 1110 | ] 1111 | }, 1112 | { 1113 | "cell_type": "code", 1114 | "execution_count": 47, 1115 | "metadata": {}, 1116 | "outputs": [ 1117 | { 1118 | "data": { 1119 | "text/plain": [ 1120 | "5" 1121 | ] 1122 | }, 1123 | "execution_count": 47, 1124 | "metadata": {}, 1125 | "output_type": "execute_result" 1126 | } 1127 | ], 1128 | "source": [ 1129 | "x.first()" 1130 | ] 1131 | }, 1132 | { 1133 | "cell_type": "code", 1134 | "execution_count": 48, 1135 | "metadata": {}, 1136 | "outputs": [ 1137 | { 1138 | "data": { 1139 | "text/plain": [ 1140 | "5" 1141 | ] 1142 | }, 1143 | "execution_count": 48, 1144 | "metadata": {}, 1145 | "output_type": "execute_result" 1146 | } 1147 | ], 1148 | "source": [ 1149 | "x.dequeue()" 1150 | ] 1151 | }, 1152 | { 1153 | "cell_type": "code", 1154 | "execution_count": 49, 1155 | "metadata": {}, 1156 | "outputs": [ 1157 | { 1158 | "data": { 1159 | "text/plain": [ 1160 | "4" 1161 | ] 1162 | }, 1163 | "execution_count": 49, 1164 | "metadata": {}, 1165 | "output_type": "execute_result" 1166 | } 1167 | ], 1168 | "source": [ 1169 | "x.dequeue()" 1170 | ] 1171 | }, 1172 | { 1173 | "cell_type": "code", 1174 | "execution_count": 50, 1175 | "metadata": {}, 1176 | "outputs": [], 1177 | "source": [ 1178 | "x.enqueue(10)" 1179 | ] 1180 | }, 1181 | { 1182 | "cell_type": "code", 1183 | "execution_count": 51, 1184 | "metadata": {}, 1185 | "outputs": [ 1186 | { 1187 | "data": { 1188 | "text/plain": [ 1189 | "3" 1190 | ] 1191 | }, 1192 | "execution_count": 51, 1193 | "metadata": {}, 1194 | "output_type": "execute_result" 1195 | } 1196 | ], 1197 | "source": [ 1198 | "x.dequeue()" 1199 | ] 1200 | }, 1201 | { 1202 | "cell_type": "code", 1203 | "execution_count": 52, 1204 | "metadata": {}, 1205 | "outputs": [ 1206 | { 1207 | "data": { 1208 | "text/plain": [ 1209 | "10" 1210 | ] 1211 | }, 1212 | "execution_count": 52, 1213 | "metadata": {}, 1214 | "output_type": "execute_result" 1215 | } 1216 | ], 1217 | "source": [ 1218 | "x.first()" 1219 | ] 1220 | }, 1221 | { 1222 | "cell_type": "code", 1223 | "execution_count": 53, 1224 | "metadata": {}, 1225 | "outputs": [ 1226 | { 1227 | "data": { 1228 | "text/plain": [ 1229 | "10" 1230 | ] 1231 | }, 1232 | "execution_count": 53, 1233 | "metadata": {}, 1234 | "output_type": "execute_result" 1235 | } 1236 | ], 1237 | "source": [ 1238 | "x.dequeue()" 1239 | ] 1240 | }, 1241 | { 1242 | "cell_type": "code", 1243 | "execution_count": 54, 1244 | "metadata": {}, 1245 | "outputs": [ 1246 | { 1247 | "name": "stdout", 1248 | "output_type": "stream", 1249 | "text": [ 1250 | "proper error\n" 1251 | ] 1252 | } 1253 | ], 1254 | "source": [ 1255 | "try:\n", 1256 | " x.dequeue()\n", 1257 | "except Empty:\n", 1258 | " print(\"proper error\")" 1259 | ] 1260 | } 1261 | ], 1262 | "metadata": { 1263 | "kernelspec": { 1264 | "display_name": "Python 3", 1265 | "language": "python", 1266 | "name": "python3" 1267 | }, 1268 | "language_info": { 1269 | "codemirror_mode": { 1270 | "name": "ipython", 1271 | "version": 3 1272 | }, 1273 | "file_extension": ".py", 1274 | "mimetype": "text/x-python", 1275 | "name": "python", 1276 | "nbconvert_exporter": "python", 1277 | "pygments_lexer": "ipython3", 1278 | "version": "3.8.3" 1279 | } 1280 | }, 1281 | "nbformat": 4, 1282 | "nbformat_minor": 4 1283 | } 1284 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | ![logo](assets/logo.png) 4 | 5 |
6 | 7 | Brief, very brief summary of ***the Data Structures and Algorithms in Python*** book and worked solutions. 8 | 9 | ![Data Structures and Algorithms in Python](https://media.wiley.com/product_data/coverImage300/75/11182902/1118290275.jpg) 10 | 11 | ## Environment / Installation 12 | 13 | * Python 3.8 14 | 15 | Intall dependencies for this repo. 16 | 17 | ```bash 18 | $ pip install -r requirements.txt 19 | ``` 20 | 21 | ## Contents 22 | 23 | |Chapter|Contents|Exercise| 24 | |---|---|---| 25 | |Chapter 01| N/A |[Notebook](https://github.com/jihoonerd/Data_Structures_and_Algorithms_in_Python/blob/main/Exercises/Chapter_01.ipynb)| 26 | |Chapter 02|[Notebook](https://github.com/jihoonerd/Data_Structures_and_Algorithms_in_Python/blob/main/Contents/Chapter_02.ipynb)|[Notebook](https://github.com/jihoonerd/Data_Structures_and_Algorithms_in_Python/blob/main/Exercises/Chapter_02.ipynb)| 27 | |Chapter 03|[Notebook](https://github.com/jihoonerd/Data_Structures_and_Algorithms_in_Python/blob/main/Contents/Chapter_03.ipynb)|[Notebook](https://github.com/jihoonerd/Data_Structures_and_Algorithms_in_Python/blob/main/Exercises/Chapter_03.ipynb)| 28 | |Chapter 04|[Notebook](https://github.com/jihoonerd/Data_Structures_and_Algorithms_in_Python/blob/main/Contents/Chapter_04.ipynb)|[Notebook](https://github.com/jihoonerd/Data_Structures_and_Algorithms_in_Python/blob/main/Exercises/Chapter_04.ipynb)| 29 | |Chapter 05|[Notebook](https://github.com/jihoonerd/Data_Structures_and_Algorithms_in_Python/blob/main/Contents/Chapter_05.ipynb)|[Notebook](https://github.com/jihoonerd/Data_Structures_and_Algorithms_in_Python/blob/main/Exercises/Chapter_05.ipynb)| 30 | |Chapter 06|[Notebook](https://github.com/jihoonerd/Data_Structures_and_Algorithms_in_Python/blob/main/Contents/Chapter_06.ipynb)|[Notebook](https://github.com/jihoonerd/Data_Structures_and_Algorithms_in_Python/blob/main/Exercises/Chapter_06.ipynb)| 31 | |Chapter 07|[Notebook](https://github.com/jihoonerd/Data_Structures_and_Algorithms_in_Python/blob/main/Contents/Chapter_07.ipynb)|[Notebook](https://github.com/jihoonerd/Data_Structures_and_Algorithms_in_Python/blob/main/Exercises/Chapter_07.ipynb)| 32 | |Chapter 08|[Notebook](https://github.com/jihoonerd/Data_Structures_and_Algorithms_in_Python/blob/main/Contents/Chapter_08.ipynb)|[Notebook](https://github.com/jihoonerd/Data_Structures_and_Algorithms_in_Python/blob/main/Exercises/Chapter_08.ipynb)| 33 | |Chapter 09|[Notebook](https://github.com/jihoonerd/Data_Structures_and_Algorithms_in_Python/blob/main/Contents/Chapter_09.ipynb)|[Notebook](https://github.com/jihoonerd/Data_Structures_and_Algorithms_in_Python/blob/main/Exercises/Chapter_09.ipynb)| 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jihoonerd/Data_Structures_and_Algorithms_in_Python/88920c1cbdb655c5bfe18ad3e74624d6745ab625/assets/logo.png -------------------------------------------------------------------------------- /images/Fig2.7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jihoonerd/Data_Structures_and_Algorithms_in_Python/88920c1cbdb655c5bfe18ad3e74624d6745ab625/images/Fig2.7.png -------------------------------------------------------------------------------- /images/Fig7.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jihoonerd/Data_Structures_and_Algorithms_in_Python/88920c1cbdb655c5bfe18ad3e74624d6745ab625/images/Fig7.1.png -------------------------------------------------------------------------------- /images/Fig7.8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jihoonerd/Data_Structures_and_Algorithms_in_Python/88920c1cbdb655c5bfe18ad3e74624d6745ab625/images/Fig7.8.png -------------------------------------------------------------------------------- /images/Fig7.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jihoonerd/Data_Structures_and_Algorithms_in_Python/88920c1cbdb655c5bfe18ad3e74624d6745ab625/images/Fig7.9.png -------------------------------------------------------------------------------- /images/improper_tree.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jihoonerd/Data_Structures_and_Algorithms_in_Python/88920c1cbdb655c5bfe18ad3e74624d6745ab625/images/improper_tree.png -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | argon2-cffi==21.3.0 2 | argon2-cffi-bindings==21.2.0 3 | asttokens==2.0.8 4 | attrs==22.1.0 5 | backcall==0.2.0 6 | beautifulsoup4==4.11.1 7 | bleach==5.0.1 8 | certifi==2024.7.4 9 | cffi==1.15.1 10 | chardet==3.0.4 11 | cycler==0.11.0 12 | dbus-python==1.2.16 13 | debugpy==1.6.3 14 | decorator==5.1.1 15 | defusedxml==0.7.1 16 | distro==1.4.0 17 | entrypoints==0.4 18 | executing==0.10.0 19 | fastjsonschema==2.16.1 20 | fonttools==4.43.0 21 | idna==3.7 22 | importlib-metadata==4.12.0 23 | importlib-resources==5.9.0 24 | ipykernel==6.15.1 25 | ipython==8.10.0 26 | ipython-genutils==0.2.0 27 | ipywidgets==8.0.1 28 | jedi==0.18.1 29 | Jinja2==3.1.6 30 | jsonschema==4.14.0 31 | jupyter==1.0.0 32 | jupyter-client==7.3.4 33 | jupyter-console==6.4.4 34 | jupyter-core==4.11.2 35 | jupyterlab-pygments==0.2.2 36 | jupyterlab-widgets==3.0.2 37 | kiwisolver==1.4.4 38 | lxml==4.9.1 39 | MarkupSafe==2.1.1 40 | matplotlib==3.5.3 41 | matplotlib-inline==0.1.6 42 | mistune==2.0.4 43 | nbclient==0.6.7 44 | nbconvert==7.0.0 45 | nbformat==5.4.0 46 | nest-asyncio==1.5.5 47 | notebook==6.4.12 48 | numpy==1.23.2 49 | packaging==21.3 50 | pandas==1.4.3 51 | pandocfilters==1.5.0 52 | parso==0.8.3 53 | pexpect==4.8.0 54 | pickleshare==0.7.5 55 | Pillow==10.3.0 56 | pkgutil_resolve_name==1.3.10 57 | prometheus-client==0.14.1 58 | prompt-toolkit==3.0.30 59 | psutil==5.9.1 60 | ptyprocess==0.7.0 61 | pure-eval==0.2.2 62 | pycparser==2.21 63 | Pygments==2.15.0 64 | PyGObject==3.36.0 65 | pyparsing==3.0.9 66 | pyrsistent==0.18.1 67 | python-dateutil==2.8.2 68 | pytz==2022.2.1 69 | pyzmq==23.2.1 70 | qtconsole==5.3.1 71 | QtPy==2.2.0 72 | requests==2.32.0 73 | scipy==1.10.0 74 | seaborn==0.11.2 75 | Send2Trash==1.8.0 76 | six==1.14.0 77 | soupsieve==2.3.2.post1 78 | ssh-import-id==5.10 79 | stack-data==0.4.0 80 | terminado==0.15.0 81 | tinycss2==1.1.1 82 | tornado==6.5.1 83 | traitlets==5.3.0 84 | ufw==0.36 85 | urllib3==1.26.19 86 | wcwidth==0.2.5 87 | webencodings==0.5.1 88 | widgetsnbextension==4.0.2 89 | zipp==3.19.1 90 | --------------------------------------------------------------------------------