├── .gitignore ├── Chapter1 ├── 1-Abstract_Data_Types.ipynb ├── README.md ├── checkdates.py ├── checkdates2.py ├── date.py ├── linearbag.py ├── linesegment.py ├── point.py ├── studentfile.py └── studentreport.py ├── Chapter2 ├── 2-Arrays.ipynb ├── README.md ├── StudentGrades.txt ├── array_class.py ├── countOccurrences.py ├── examGrades.py ├── gameOfLife.md ├── gameoflife.py ├── grayscale_class.py ├── life.py ├── matrix_class.py ├── textfile.txt └── vector_class.py ├── Chapter3 ├── 3-Sets_and_Maps.ipynb ├── README.md ├── array_class.py ├── linearmap.py ├── linearset.py ├── multiArray_class.py ├── salesReport.py └── triangleArray_class.py ├── Chapter4 ├── 4-Algorithm_Analysis.ipynb ├── README.md └── sparsematrix.py ├── Chapter5 ├── 5-Searching_and_Sorting.ipynb ├── README.md ├── binarysearch.py ├── binaryset.py ├── bubblesort.py ├── findsortedposition.py ├── insertionSort.py ├── linearsearch.py ├── mergeSortedList.py └── selectionsort.py ├── Chapter6 ├── 6-Linked_Structures.ipynb ├── README.md ├── figures │ ├── addnode2linkedlist.png │ └── removeANode.png ├── llistbag.py ├── llistbag_test.py ├── llistsparse.py ├── llistsparse_test.py ├── node.py ├── polynomial.py ├── polynomial_test.py ├── singlelinkedlist.py └── singlelinkedlist_test.py ├── Chapter7 ├── 7-Stacks.ipynb └── README.md ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | 27 | # PyInstaller 28 | # Usually these files are written by a python script from a template 29 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 30 | *.manifest 31 | *.spec 32 | 33 | # Installer logs 34 | pip-log.txt 35 | pip-delete-this-directory.txt 36 | 37 | # Unit test / coverage reports 38 | htmlcov/ 39 | .tox/ 40 | .coverage 41 | .coverage.* 42 | .cache 43 | nosetests.xml 44 | coverage.xml 45 | *,cover 46 | .hypothesis/ 47 | 48 | # Translations 49 | *.mo 50 | *.pot 51 | 52 | # Django stuff: 53 | *.log 54 | local_settings.py 55 | 56 | # Flask stuff: 57 | instance/ 58 | .webassets-cache 59 | 60 | # Scrapy stuff: 61 | .scrapy 62 | 63 | # Sphinx documentation 64 | docs/_build/ 65 | 66 | # PyBuilder 67 | target/ 68 | 69 | # IPython Notebook 70 | .ipynb_checkpoints 71 | 72 | # pyenv 73 | .python-version 74 | 75 | # celery beat schedule file 76 | celerybeat-schedule 77 | 78 | # dotenv 79 | .env 80 | 81 | # virtualenv 82 | venv/ 83 | ENV/ 84 | 85 | # Spyder project settings 86 | .spyderproject 87 | 88 | # Rope project settings 89 | .ropeproject 90 | -------------------------------------------------------------------------------- /Chapter1/1-Abstract_Data_Types.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Abstract Data Types\n", 8 | "\n", 9 | "## Introduction\n", 10 | "\n", 11 | "There are **primitive data types** provided by the programming language as default and they come in two categories:\n", 12 | "\n", 13 | "- __simple data types__ consist of values that are in the most basic form and cannot be decomposed into smaller parts. (*Interger and real type*)\n", 14 | "- __complex data types__ are constructed of multiple components consisting of simple types or other complex types. (*objects, strings, lists, dictionaries*)\n", 15 | "\n", 16 | "> Most languages allow for the construction of additional data types, known as **user-defined types**. \n", 17 | "\n", 18 | "### Abstractions\n", 19 | "\n", 20 | "An Abstraction is a mechanism for separating the properties of an object and restricting the focus to those relevant in the current context. _They help manage the complex problems and complex data types._ \n", 21 | "\n", 22 | "- **Procedural Abstraction** is a mental model of what we want a subprogram to do (but **not** how to do it)\n", 23 | "- **Data Abstraction** s a mental model of what can be done to a collection of data. It deliberately excludes details of how to do it.\n", 24 | "\n", 25 | "![Levels of abstraction used with integer arithmetic](https://cloud.githubusercontent.com/assets/550518/18416451/1b933294-7848-11e6-951a-78b09a8a73e8.png)\n", 26 | "\n", 27 | "In the figure above the levels of abstraction is shown. In the Hardware level the abstraction is none, however when we go upper levels we have more and more complex abstractions to be able to make it user friendly.\n", 28 | "\n", 29 | "> In Python Big integers are provided as part of the language itself.\n", 30 | "\n", 31 | "\n", 32 | "### Abstract Data Type\n", 33 | "\n", 34 | "An abstract data type is a programmer-defined data type that specifies a set of data values and a collection of well-defined operations that can be performed on thos values.\n", 35 | "\n", 36 | "Abstract data types are like black boxes that they hide the how to information to themselves and make their functionality available to users by requiring them to use their intefaces/set of operations. Set of operations canbe grouped into 4 category:\n", 37 | "\n", 38 | "- **Constructors:** Create an initialize new instancese of the ADT.\n", 39 | "- **Accessors:** Return data contained in an instance without modifying it.\n", 40 | "- **Mutators:** Modify the contents of an ADT instance.\n", 41 | "- ** Iterators:** Process individual data components sequentially. \n", 42 | "\n", 43 | "> Implementation details are hidden in the Abstract Data type.\n", 44 | "\n" 45 | ] 46 | } 47 | ], 48 | "metadata": { 49 | "kernelspec": { 50 | "display_name": "Python 2", 51 | "language": "python", 52 | "name": "python2" 53 | }, 54 | "language_info": { 55 | "codemirror_mode": { 56 | "name": "ipython", 57 | "version": 2 58 | }, 59 | "file_extension": ".py", 60 | "mimetype": "text/x-python", 61 | "name": "python", 62 | "nbconvert_exporter": "python", 63 | "pygments_lexer": "ipython2", 64 | "version": "2.7.11" 65 | } 66 | }, 67 | "nbformat": 4, 68 | "nbformat_minor": 0 69 | } 70 | -------------------------------------------------------------------------------- /Chapter1/README.md: -------------------------------------------------------------------------------- 1 | ## Chapter 1: Abstract Data Types 2 | 3 | ### To Do List: 4 | 5 | - [x] Write chapter notes to ipynb 6 | - [x] Define and Implement Date ADT 7 | - [x] Write a small snippet to verify usage of Date ADT 8 | - [x] Define and Implement Bag ADT 9 | - [x] Write a small snippet to verify usage of Bag ADT 10 | - [ ] **(Important)** Write Iterator implementation for classes into notes and understand it 11 | - [x] Define and Implement StudentFileReader ADT 12 | - [x] Write a small snippet to verify usage of StudentFileReader ADT 13 | - [x] Complete Date ADT by adding more methods 14 | - [x] Implement function called ```printCalendar()``` 15 | - [ ] Define and Implement Counter ADT 16 | - [ ] Write a small snippet to verify usage of Counter ADT 17 | - [x] Define and Implement Point ADT 18 | - [x] Write a small snippet to verify usage of Point ADT 19 | - [ ] Define and Implement StudentFileWriter ADT 20 | - [ ] Write a small snippet to verify usage of StudentFileWriter ADT 21 | - [ ] Define and Implement GrabBag ADT 22 | - [ ] Write a small snippet to verify usage of GrabBag ADT 23 | - [ ] Define and Implement CountingBag ADT 24 | - [ ] Write a small snippet to verify usage of CountingBag ADT 25 | -------------------------------------------------------------------------------- /Chapter1/checkdates.py: -------------------------------------------------------------------------------- 1 | # Extracts a collection of birth dates from the user and determines 2 | # if each individual is at least 21 years of age. 3 | from date import Date 4 | 5 | def main(): 6 | # Date before which a person must have been born to be 21 or older 7 | bornBefore = Date(6, 1, 1995) 8 | 9 | 10 | # Extract birth dates from the user and determine if 21 or older. 11 | date = promptAndExtractDate() 12 | while date is not None: 13 | if date <= bornBefore: 14 | print("Is at least 21 years of age: " + str(date)) 15 | date = promptAndExtractDate() 16 | 17 | 18 | # Prompts for and extracts the Gregorian date components. Returns a 19 | # Date object or None when the user has finished entering dates. 20 | def promptAndExtractDate(): 21 | print( "Enter a birth date." ) 22 | month = int(input("month (0 to quit): ")) 23 | if month == 0: 24 | return None 25 | else: 26 | day = int(input("day: ")) 27 | year = int(input("year: ")) 28 | return Date(month, day, year) 29 | 30 | 31 | # Call the main route in terminal 32 | if __name__ == '__main__': 33 | main() 34 | -------------------------------------------------------------------------------- /Chapter1/checkdates2.py: -------------------------------------------------------------------------------- 1 | # Modified version of checkdates.py 2 | # Now we can store dates from user in our bag 3 | 4 | from linearbag import Bag 5 | from date import Date 6 | 7 | def main(): 8 | bornBefore = Date(6, 1, 1995) 9 | bag = Bag() 10 | 11 | # Extract dates from the user and place them in the bag. 12 | date = promptAndExtractDate() 13 | while date is not None: 14 | bag.add(date) 15 | date = promptAndExtractDate() 16 | 17 | # Iterate over the bag and check the age. 18 | for date in bag: 19 | if date <= bornBefore: 20 | print("Is at least 21 years of age: " + str(date)) 21 | 22 | 23 | 24 | # Prompts for and extracts the Gregorian date components. Returns a 25 | # Date object or None when the user has finished entering dates. 26 | def promptAndExtractDate(): 27 | print( "Enter a birth date." ) 28 | month = int(input("month (0 to quit): ")) 29 | if month == 0: 30 | return None 31 | else: 32 | day = int(input("day: ")) 33 | year = int(input("year: ")) 34 | return Date(month, day, year) 35 | 36 | if __name__ == '__main__': 37 | main() 38 | -------------------------------------------------------------------------------- /Chapter1/date.py: -------------------------------------------------------------------------------- 1 | # Implements a proleptic Gregorian calendar date as a Julian day number 2 | 3 | class Date: 4 | # Creates an object instance for the specified Gregorian date. 5 | def __init__(self, month, day, year): 6 | self._julianDay = 0 7 | assert self._isValidGregorian(month, day, year), "Invalid Gregorian date." 8 | 9 | # If first line of the equation, T = (M-14)/12, has to be changed 10 | # since Python's implemenation of integer division is not the same 11 | # as the mathematical definition. 12 | tmp = 0 13 | if month < 3: 14 | tmp = -1 15 | self._julianDay = day - 32075 + \ 16 | (1461 * (year + 4800 + tmp) // 4) + \ 17 | (367 * (month - 2 - tmp * 12) // 12) - \ 18 | (3 * ((year + 4900 + tmp) // 100) // 4) 19 | 20 | # Returns the date as a string in Gregorian format. 21 | def __str__(self): 22 | month, day, year = self._toGregorian() 23 | return "%02d/%02d/%04d" % (month, day, year) 24 | 25 | # Logically compares the two dates. 26 | def __eq__(self, otherDate): 27 | return self._julianDay == otherDate._julianDay 28 | 29 | def __lt__(self, otherDate): 30 | return self._julianDay < otherDate._julianDay 31 | 32 | def __le__(self, otherDate): 33 | return self._julianDay <= otherDate._julianDay 34 | 35 | # Returns the month as an integer 36 | def month(self): 37 | return (self._toGregorian())[0] # Returns M from (M, d, y) 38 | 39 | # Returns the day as an integer 40 | def day(self): 41 | return (self._toGregorian())[1] # Returns D from (m, D, y) 42 | 43 | # Returns the year as an integer 44 | def year(self): 45 | return (self._toGregorian())[2] # Returns Y from (m, d, Y) 46 | 47 | # Returns day of the week as and int between 0 (mon), and 6 (sun) 48 | def dayOfWeek(self): 49 | month, day, year = self._toGregorian() 50 | if month < 3: 51 | month = month + 12 52 | year = year - 1 53 | return ((13 * month + 3) // 5 + day + \ 54 | year + year // 4 - year // 100 + year // 400) % 7 55 | 56 | # Returns the month name from given date 57 | def monthName(self): 58 | """ 59 | Returns the month's name 60 | 1 -> January 61 | 12 -> December 62 | """ 63 | monthdict = {1:"January", 2:"February", 3:"March", 4:"April", 5:"May", 64 | 6:"June", 7:"July", 8:"August", 9:"September", 65 | 10:"October", 11:"November", 12:"December"} 66 | month = self._toGregorian()[0] 67 | return monthdict[month] 68 | 69 | # Checks the year if it's leap year or not 70 | def isLeapYear(self): 71 | """ 72 | Leap Year follows: 73 | The year can be evenly divided by 4; 74 | If the year can be evenly divided by 100, it is NOT a leap year, unless; 75 | The year is also evenly divisible by 400. Then it is a leap year. 76 | """ 77 | year = self._toGregorian()[3] 78 | leapyear = False 79 | if year % 4 == 0: 80 | leapyear = True 81 | if year % 100 == 0: 82 | leapyear = False 83 | if year % 400 == 0: 84 | leapyear = True 85 | return leapyear 86 | 87 | # Returns the number of days in given month 88 | def numDays(self): 89 | monthLenDict = {1:31, 2:28, 3:31, 4:30, 5:31, 6:30, 7:31, 8:31, 9:30, 90 | 10:31, 11:31, 12:30} 91 | month = self._toGregorian()[0] 92 | if month == 2 and self.isLeapYear(): 93 | return 29 94 | else: 95 | return monthLenDict[month] 96 | 97 | # Advances the date by given number of days 98 | def advanceBy(self, numDays): 99 | self._julianDay += numDays 100 | 101 | # Returns the name day of week from given date. 102 | def dayOfWeekName(self): 103 | weekNames={0:"Monday", 1:"Tuesday", 2:"Wednesday", 3:"Thursday", 104 | 4:"Friday", 5:"Saturday", 6:"Sunday"} 105 | return weekNames[self.dayOfWeek()] 106 | 107 | # Returns the day of year from 0 to 365/366 108 | def dayOfYear(self): 109 | month,day,year = self._toGregorian() 110 | dayYear=0 111 | if self.isLeapYear(year): 112 | monthList = [0,31,29,31,30,31,30,31,31,30,31,30,31] 113 | for i in range(0,month): 114 | dayYear+=monthList[i] 115 | return dayYear + day 116 | else: 117 | monthList = [0,31,28,31,30,31,30,31,31,30,31,30,31] 118 | for i in range(0,month): 119 | dayYear+=monthList[i] 120 | return dayYear + day 121 | 122 | # Checks if the date is a weekday 123 | def isWeekDay(self): 124 | return (self.dayOfWeek()>=0 and self.dayOfWeek()<=4) 125 | 126 | # Checks if the date is a spring or autumn equinox 127 | def isEquinox( self ): 128 | return (self.day() == 20 and self.month() == 3) or \ 129 | (self.day() == 22 and self.month() == 9) 130 | # Check if the date is the summer or winter solstice 131 | def isSolstice( self ): 132 | return (self.day() == 21 and self.month() == 6) or \ 133 | (self.day() == 22 and self.month() == 12) 134 | 135 | # Logically compares the two dates. 136 | def comparable(self, otherDate): 137 | if self.__lt__(otherDate): 138 | return -1 139 | if self.__eq__(otherDate): 140 | return 0 141 | else: 142 | return 1 143 | 144 | # Checks if date is valid Gregorian or not 145 | def _isValidGregorian(self, month, day, year): 146 | if month < 1 or month > 12: 147 | return False 148 | if month == 1 or month == 3 or month == 5: 149 | if day < 1 or day > 31: 150 | return False 151 | elif month == 8 or month == 10 or month == 12: 152 | if day < 1 or day > 31: 153 | return False 154 | elif month == 4 or month == 6 or month == 9 or month == 11: 155 | if day < 1 or day > 30: 156 | return False 157 | elif month == 2: #I explicitly do not account for leap years 158 | if day < 1 or day > 28: 159 | return False 160 | 161 | # string method with additional calendar divider 162 | def asGreogrian( self, divchar = '/'): 163 | month, day, year = self._toGregorian() 164 | return '%02d%s%02d%s%04d' % (month, divchar, day, divchar, year) 165 | 166 | # Returns the Gregorian date as a tuple: (month, day, year) 167 | def _toGregorian(self): 168 | A = self._julianDay + 68569 169 | B = 4 * A //146097 170 | A = A - (146097 * B + 3) // 4 171 | year = 4000 * (A + 1) // 1461001 172 | A = A - (1461 * year // 4) + 31 173 | month = 80 * A // 2447 174 | day = A - (2447 * month // 80) 175 | A = month // 11 176 | month = month + 2 - (12 * A) 177 | year = 100 * (B - 49) + year + A 178 | return month, day, year 179 | 180 | def printCalendar(self): 181 | month,day,year = self._toGregorian() 182 | self.advanceBy(-day+1) 183 | month,day,year = self._toGregorian() 184 | print self.dayOfWeekName() 185 | print (self.monthName()+" "+str(self.year())).center(50) 186 | print "Su Mo Tu We Th Fr Sa".center(50) 187 | result="" 188 | if self.isLeapYear(year): 189 | monthList = [0,31,29,31,30,31,30,31,31,30,31,30,31] 190 | else: 191 | monthList = [0,31,28,31,30,31,30,31,31,30,31,30,31] 192 | for i in range(1,monthList[month]+1): 193 | result+=str(i) 194 | print result.center(50) 195 | -------------------------------------------------------------------------------- /Chapter1/linearbag.py: -------------------------------------------------------------------------------- 1 | # Implements the Bag ADT container using Python Lists 2 | class Bag: 3 | # Constructs an empty bag. 4 | def __init__(self): 5 | self._theItems = list() 6 | 7 | # Returns the number of items in the bag. 8 | def __len__(self): 9 | return len(self._theItems) 10 | 11 | # Determines if an item is contained in the bag. 12 | def __contains__(self, item): 13 | return item in self._theItems 14 | 15 | # Adds a new item to the bag. 16 | def add(self, item): 17 | self._theItems.append(item) 18 | 19 | # Removes and returns an instance of the item from the bag. 20 | def remove(self, item): 21 | assert item in self._theItems, "The item must be in the bag" 22 | ndx = self._theItems.index(item) 23 | return self._theItems.pop(ndx) 24 | 25 | # Returns an iterator for traversing the list of items. 26 | def __iter__(self, item): 27 | return _BagIterator(self._theItems) 28 | 29 | # An iterator for the bag ADT implemented as a Python List 30 | class _BagIterator: 31 | def __init__(self, theList): 32 | self._bagItems = theList 33 | self._curItem = 0 34 | 35 | def __iter__(self): 36 | return self 37 | 38 | def __next__(self): 39 | if self._curItem < len(self._bagItems): 40 | item = self._bagItems[self._curItem] 41 | self._curItem += 1 42 | else: 43 | raise StopIteration 44 | -------------------------------------------------------------------------------- /Chapter1/linesegment.py: -------------------------------------------------------------------------------- 1 | from point import Point 2 | 3 | class LineSegment: 4 | # Creates a new line segment instance defined by the two Point objects 5 | def __init__(self, ptA, ptB): 6 | self._pointA = ptA 7 | self._pointB = ptB 8 | 9 | # Returns the first endpoint of the line 10 | def endPointA(self): 11 | return self._pointA 12 | 13 | # Returns the second endpoint of the line 14 | def endPointB(self): 15 | return self._pointB 16 | 17 | # Returns the length of the line calculated Euclidean distance 18 | # Using Point ADT distance method 19 | def __len__(self): 20 | return self.endPointA().distance(self.endPointB()) 21 | 22 | # String representation of point A and B together 23 | # Format (Ax, Ay)#(Bx, By) 24 | def __str__(self): 25 | return str(self.endPointA() ) + "#" + str(self.endPointB()) 26 | 27 | # Checks if the line segment parallel to y-axis 28 | def isVertical(self): 29 | return self.endPointA()._xcoord == self.endPointB()._xcoord 30 | 31 | # Check if the line segment parallel to x-axis 32 | def isHorizontal(self): 33 | return self.endPointA()._ycoord == self.endPointB()._ycoord 34 | 35 | # Checks if the line parallel to another line given 36 | def isParallel(self, otherLine): 37 | # If there is not a slope other must be None 38 | if self.slope() is None: 39 | return otherLine.slope is None 40 | 41 | return self.slope() == otherLine.slope() 42 | 43 | # Check if the line perpendicular to another line given 44 | def isPerpendicular(self, otherLine): 45 | if self.slope() is None: 46 | return otherLine.slope == 0 47 | elif self.slope() == 0: 48 | return otherLine.slope is None 49 | else: 50 | return (self.slope() + otherLine.slope()) == 1 51 | 52 | # Returns the slope of the line segment given as the rise over the run 53 | def slope(self): 54 | return (self.endPointA()._ycoord - self.endPointB()._ycoord) \ 55 | / (self.endPointA._xcoord - self.endPointB()._ycoord) 56 | 57 | # Shifts the line segment by xInc amount along the x-axis 58 | # and yInc amount along the y-axis 59 | def shif(self, xInc, yInc): 60 | # Using Point ADT's slope function 61 | self._pointA.shift(xInc, yInc) 62 | self._pointB.shift(xInc, yInc) 63 | 64 | # Returns the midpoint of the line segment as a Point object 65 | def midPoint(self): 66 | x = (self.endPointA()._xcoord + self.endPointB()._xcoord) / 2 67 | y = (self.endPointA()._ycoord + self.endPointB()._ycoord) / 2 68 | midPoint = Point(x, y) 69 | return midPoint 70 | -------------------------------------------------------------------------------- /Chapter1/point.py: -------------------------------------------------------------------------------- 1 | from __future__ import division 2 | from math import sqrt 3 | 4 | class Point: 5 | # Constructs the point object with x and y values 6 | def __init__( self, x, y ): 7 | self._xcoord = x 8 | self._ycoord = y 9 | 10 | # Returns the x coordinate 11 | def getX( self ): 12 | return self._xcoord 13 | 14 | # Returns the y coordinate 15 | def getY( self ): 16 | return self._ycoord 17 | 18 | # shifts x and y coordinates with given values xInc, yInc 19 | def shift( self, xInc, yInc ): 20 | self._xcoord += xInc 21 | self._ycoord += yInc 22 | 23 | # Calculates and returns the distance between x,y point to given pointB 24 | def distance( self, pointB ): 25 | xDiff = self._xcoord - pointB._xcoord 26 | yDiff = self._ycoord - pointB._ycoord 27 | return sqrt( xDiff ** 2 + yDiff ** 2 ) 28 | -------------------------------------------------------------------------------- /Chapter1/studentfile.py: -------------------------------------------------------------------------------- 1 | # Implementation of the StudentFileReader ADT using a text file as the 2 | # input source in which each field is stored on a separate line. 3 | 4 | class StudentFileReader: 5 | # Create a new student reader instance. 6 | def __init__(self, inputSrc): 7 | self._inputSrc = inputSrc 8 | self._inputSrc = None 9 | 10 | # Open a connection to the input file. 11 | def open(self): 12 | self._inputFile = open(self._inputSrc, "r") 13 | 14 | # Close the connection to the input file. 15 | def close(self): 16 | self._inputFile.close() 17 | self._inputFile = None 18 | 19 | # Extract all student record and store them in a list. 20 | def fetchAll(self): 21 | theRecords = list() 22 | student = self.fethcRecord() 23 | while student != None: 24 | theRecords.append(student) 25 | student = self.fethcRecord() 26 | return theRecords 27 | 28 | # Extract the next student record from the file. 29 | def fetchRecord(self): 30 | # Read the first line of the record. 31 | line = self._inputFile.readline() 32 | if line == "": 33 | return None 34 | 35 | # If there is another record, create a storage object and fill it. 36 | student = StudentRecord() 37 | student.idNum = int(line) 38 | student.firstName = self._inputFile.readline().rstrip() 39 | student.lastName = self._inputFile.readline().rstrip() 40 | student.classCode = int(self._inputFile.readline()) 41 | student.gpa = float(self._inputFile.readline()) 42 | return student 43 | 44 | 45 | 46 | 47 | # Storage class used for an individual student record. 48 | class StudentRecord(): 49 | def __init__(self): 50 | self.idNum = 0 51 | self.firstName = None 52 | self.lastName = None 53 | self.classCode = 0 54 | self.gpa = 0.0 55 | -------------------------------------------------------------------------------- /Chapter1/studentreport.py: -------------------------------------------------------------------------------- 1 | # Produces a student report from data extracted from and external source. 2 | from studentfile import StudentFileReader 3 | 4 | # Name of the file to open. 5 | FILE_NAME = "students.txt" 6 | 7 | def main(): 8 | # Extract the students records from the given text file. 9 | reader = StudentFileReader(FILE_NAME) 10 | reader.open() 11 | studentList = reader.fetchAll() 12 | reader.close() 13 | 14 | # Sort the list by id number. Each object is passed to the lambda 15 | # expression which returns the idNum field of the object. 16 | studentList.sort(key = lambda rec: rec.idNum) 17 | 18 | # Print the student report. 19 | printReport(studentList) 20 | 21 | # Prints the student report. 22 | def printReport(theList): 23 | # The class names associated with the class codes. 24 | classNames = (None, "Freshman", "Sophomore", "Junior", "Senior") 25 | 26 | # print the header. 27 | print("LIST OF STUDENTS".center(50)) 28 | print("") 29 | print("%-5s %-25s %-10s %-4s" % ('ID', 'NAME', 'CLASS', 'GPA')) 30 | print("%5s %25s %10s %4s" % ('-' * 5, '-' * 25, '-' * 10, '-' * 4)) 31 | # Print the body 32 | for record in theList: 33 | print("%5d %-25s %-10s %4.2f" % (record.idNum, \ 34 | record.lastName + ", " + record.firstName, 35 | classNames[record.classCode], record.gpa )) 36 | 37 | # Add a footer. 38 | print("-"*50) 39 | print("Number of Students: ", len(theList)) 40 | 41 | 42 | if __name__ == '__main__': 43 | main() 44 | -------------------------------------------------------------------------------- /Chapter2/2-Arrays.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Arrays\n", 8 | "\n", 9 | "\n", 10 | "Array structure is the most basic structure to store data. In this section we will learn the basics of Arrays and implement an array structure for a one-dimensional array. Then we will create two dimensional array.\n", 11 | "\n", 12 | "## The Array Structure\n", 13 | "\n", 14 | "- A __one-dimensional array__ composed of multiple sequential elements stored in contiguous bytes of memory and allows for random access to the individual elements. \n", 15 | "- Elements in the array can be accessed directly by the index number we assign while we create the array.\n", 16 | "\n", 17 | "Array structure is very similar to Python's list structure, however there are two major differences between the array and the list. \n", 18 | "\n", 19 | "- First, Array has limited number of operations, and list has large collection of operations.\n", 20 | "- Second, Array size cannot be changed after it's created, but list is flexible.\n", 21 | "\n", 22 | "Array is best usable when the number of elements are known up front. \n", 23 | "\n", 24 | "\n", 25 | "### The Array Abstract Data Type\n", 26 | "\n", 27 | "Array structure found in most programming languages as primitive type. Now we will define Array ADT to represent a one-dimensional array for use in Python that works similarly to arrays found in other languages. \n", 28 | "\n", 29 | "__Array ADT Definition__:\n", 30 | "\n", 31 | "- ```Array(size)``` : Create one-dimensional array consisting of ```size``` elements with each element initially set to None. ```size``` must be greater than zero. \n", 32 | "- ```length()``` : Returns the length or number of elements in the array\n", 33 | "- ```getitem(index)``` : Returns the value stored in the array at element position ```index```.(Must be in valid range.)\n", 34 | "- ```setitem(index, value)``` : Modifies the contents of the array element at position ```index``` to contain ```value```. \n", 35 | "- ```clearing(value)``` : Clears the array by setting every element to ```value```\n", 36 | "- ```iterator()``` : Creates and returns an iterator that can be used to traverse the elements of the array.\n", 37 | "\n", 38 | "In our ADT definition we used basic hardware level implementation and made it more abstract by adding iterator and \n", 39 | "optain size, set value, etc.\n", 40 | "\n", 41 | "Now we created our ADT in file called ```array.py```, let's use it to fill our array with random values and print them one per line. " 42 | ] 43 | }, 44 | { 45 | "cell_type": "code", 46 | "execution_count": null, 47 | "metadata": { 48 | "collapsed": false 49 | }, 50 | "outputs": [], 51 | "source": [ 52 | "from array_class import Array1D\n", 53 | "import random\n", 54 | "\n", 55 | "# Array valueList created with size of 100\n", 56 | "valueList = Array1D(100)\n", 57 | "\n", 58 | "# Filling the array with random floating-point values\n", 59 | "for i in range(len(valueList)):\n", 60 | " valueList[i] = random.random()\n", 61 | " \n", 62 | "# Print the values, one per line\n", 63 | "for value in valueList:\n", 64 | " print(value)" 65 | ] 66 | }, 67 | { 68 | "cell_type": "markdown", 69 | "metadata": {}, 70 | "source": [ 71 | "Now our Array ADT is working like charm let's use it with somewhat better implementation. Let's count the number of occurrences of each letter in a text file using Array ADT:" 72 | ] 73 | }, 74 | { 75 | "cell_type": "code", 76 | "execution_count": 3, 77 | "metadata": { 78 | "collapsed": false 79 | }, 80 | "outputs": [ 81 | { 82 | "name": "stdout", 83 | "output_type": "stream", 84 | "text": [ 85 | "A - 168 a - 3860\n", 86 | "B - 126 b - 609\n", 87 | "C - 70 c - 898\n", 88 | "D - 42 d - 2330\n", 89 | "E - 22 e - 5969\n", 90 | "F - 128 f - 890\n", 91 | "G - 34 g - 1012\n", 92 | "H - 103 h - 3492\n", 93 | "I - 156 i - 2748\n", 94 | "J - 27 j - 56\n", 95 | "K - 17 k - 390\n", 96 | "L - 105 l - 1693\n", 97 | "M - 116 m - 1000\n", 98 | "N - 43 n - 3219\n", 99 | "O - 52 o - 3909\n", 100 | "P - 44 p - 801\n", 101 | "Q - 2 q - 35\n", 102 | "R - 17 r - 2350\n", 103 | "S - 135 s - 2692\n", 104 | "T - 348 t - 4435\n", 105 | "U - 3 u - 1448\n", 106 | "V - 6 v - 323\n", 107 | "W - 150 w - 1110\n", 108 | "X - 0 x - 108\n", 109 | "Y - 22 y - 891\n", 110 | "Z - 0 z - 14\n" 111 | ] 112 | } 113 | ], 114 | "source": [ 115 | "from array_class import Array1D\n", 116 | "\n", 117 | "# Array theCounters created with size of 127 (ASCII characters)\n", 118 | "theCounters = Array1D(127)\n", 119 | "# theCounters elements initialized to 0 \n", 120 | "theCounters.clear(0)\n", 121 | "\n", 122 | "# Open the text file for reading and extract each line from the file\n", 123 | "# and iterate over each character in the line.\n", 124 | "theFile = open('textfile.txt', 'r')\n", 125 | "for line in theFile:\n", 126 | " for letter in line:\n", 127 | " code = ord(letter)\n", 128 | " theCounters[code] += 1\n", 129 | "# Close the file\n", 130 | "theFile.close()\n", 131 | "\n", 132 | "# Print the results. The uppercase letters have ASCII values in the range 65..90\n", 133 | "# the lowercase letters are in the range 97..122.\n", 134 | "for i in range(26):\n", 135 | " print(\"%c - %4d %c - %4d\" % (chr(65+i), theCounters[65+i], chr(97+i), theCounters[97+i]))" 136 | ] 137 | }, 138 | { 139 | "cell_type": "markdown", 140 | "metadata": { 141 | "collapsed": true 142 | }, 143 | "source": [ 144 | "To implement our array ADT we will use built-in module called ```ctypes``` which will give us an opportunity to implement hardware-supported array structure. \n", 145 | "\n", 146 | "The ```ctypes``` module provides a tecnique for creating arrays that can store **References** to Python objects. " 147 | ] 148 | }, 149 | { 150 | "cell_type": "code", 151 | "execution_count": 7, 152 | "metadata": { 153 | "collapsed": false 154 | }, 155 | "outputs": [ 156 | { 157 | "ename": "ValueError", 158 | "evalue": "PyObject is NULL", 159 | "output_type": "error", 160 | "traceback": [ 161 | "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", 162 | "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", 163 | "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 2\u001b[0m \u001b[0mArrayType\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mctypes\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mpy_object\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0;36m5\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 3\u001b[0m \u001b[0mslots\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mArrayType\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[0mslots\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", 164 | "\u001b[0;31mValueError\u001b[0m: PyObject is NULL" 165 | ] 166 | } 167 | ], 168 | "source": [ 169 | "import ctypes\n", 170 | "ArrayType = ctypes.py_object * 5\n", 171 | "slots = ArrayType()\n", 172 | "slots[0]" 173 | ] 174 | }, 175 | { 176 | "cell_type": "markdown", 177 | "metadata": {}, 178 | "source": [ 179 | "Now if we want to print the value of the first item in the recently created array slots, we get ValueError, which says we don't have value for referenced position. But if we assign values..." 180 | ] 181 | }, 182 | { 183 | "cell_type": "code", 184 | "execution_count": 8, 185 | "metadata": { 186 | "collapsed": true 187 | }, 188 | "outputs": [], 189 | "source": [ 190 | "slots[1] = 12\n", 191 | "slots[3] = 44\n", 192 | "slots[4] = 59\n", 193 | "slots[3] = None" 194 | ] 195 | }, 196 | { 197 | "cell_type": "code", 198 | "execution_count": 12, 199 | "metadata": { 200 | "collapsed": false 201 | }, 202 | "outputs": [ 203 | { 204 | "name": "stdout", 205 | "output_type": "stream", 206 | "text": [ 207 | "12\n" 208 | ] 209 | } 210 | ], 211 | "source": [ 212 | "print slots[1]" 213 | ] 214 | }, 215 | { 216 | "cell_type": "code", 217 | "execution_count": 11, 218 | "metadata": { 219 | "collapsed": false 220 | }, 221 | "outputs": [ 222 | { 223 | "name": "stdout", 224 | "output_type": "stream", 225 | "text": [ 226 | "None\n" 227 | ] 228 | } 229 | ], 230 | "source": [ 231 | "print slots[3]" 232 | ] 233 | }, 234 | { 235 | "cell_type": "code", 236 | "execution_count": 13, 237 | "metadata": { 238 | "collapsed": false 239 | }, 240 | "outputs": [ 241 | { 242 | "ename": "ValueError", 243 | "evalue": "PyObject is NULL", 244 | "output_type": "error", 245 | "traceback": [ 246 | "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", 247 | "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", 248 | "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0;32mprint\u001b[0m \u001b[0mslots\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m2\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", 249 | "\u001b[0;31mValueError\u001b[0m: PyObject is NULL" 250 | ] 251 | } 252 | ], 253 | "source": [ 254 | "print slots[2]" 255 | ] 256 | }, 257 | { 258 | "cell_type": "markdown", 259 | "metadata": {}, 260 | "source": [ 261 | "The size of the array never change which is really useful when you know the size of the array you will need. For class definition of array ADT check: ```array_class.py```" 262 | ] 263 | }, 264 | { 265 | "cell_type": "markdown", 266 | "metadata": {}, 267 | "source": [ 268 | "## Two-Dimensional Arrays\n", 269 | "\n", 270 | "Some problems require us to make more then 1 dimensions, in this case two-dimensional arrays are handy.\n", 271 | "\n", 272 | "We will define Array2D ADT for creating 2-D arrays, we will use some of the features directly from Array1D ADT. \n", 273 | "\n", 274 | "You can find the implementation in ```array_class.py``` file.\n", 275 | "\n", 276 | "Now let's talk about the usage of 2D arrays. Following snippets shows how to read text file and store the grades of students in the 2-D array.\n" 277 | ] 278 | }, 279 | { 280 | "cell_type": "code", 281 | "execution_count": 5, 282 | "metadata": { 283 | "collapsed": false 284 | }, 285 | "outputs": [ 286 | { 287 | "name": "stdout", 288 | "output_type": "stream", 289 | "text": [ 290 | "['90', '96', '92']\n", 291 | "['85', '91', '89']\n", 292 | "['82', '73', '84']\n", 293 | "['69', '82', '86']\n", 294 | "['95', '88', '91']\n", 295 | "['78', '64', '84']\n", 296 | "['92', '85', '89']\n", 297 | " 1: 92.00\n", 298 | " 2: 88.00\n", 299 | " 3: 79.00\n", 300 | " 4: 79.00\n", 301 | " 5: 91.00\n", 302 | " 6: 75.00\n", 303 | " 7: 88.00\n" 304 | ] 305 | } 306 | ], 307 | "source": [ 308 | "from array_class import Array2D\n", 309 | "\n", 310 | "filename = \"StudentGrades.txt\"\n", 311 | "\n", 312 | "# Open the text file for reading.\n", 313 | "gradeFile = open(filename, \"r\")\n", 314 | "\n", 315 | "# Extract the first two values which indicate the size of the array.\n", 316 | "numStudents = int(gradeFile.readline())\n", 317 | "numExams = int(gradeFile.readline())\n", 318 | "\n", 319 | "\n", 320 | "# Create the 2-D array to store the grades.\n", 321 | "examGrades = Array2D(numStudents, numExams)\n", 322 | "\n", 323 | "# Extract the grades from the remaining lines.\n", 324 | "i = 0\n", 325 | "for student in gradeFile:\n", 326 | " grades = student.split()\n", 327 | " # print grades\n", 328 | " for j in range(numExams):\n", 329 | " examGrades[i,j] = int(grades[j])\n", 330 | " i += 1\n", 331 | "\n", 332 | "# Close the text file.\n", 333 | "gradeFile.close()\n", 334 | "\n", 335 | "# Compute each student's average exam grade.\n", 336 | "for i in range(numStudents):\n", 337 | " # Tally the exam grades for the ith student.\n", 338 | " total = 0\n", 339 | " for j in range(numExams):\n", 340 | " total += examGrades[i,j]\n", 341 | "\n", 342 | " # Computer average for the ith student.\n", 343 | " examAvg = total / numExams\n", 344 | " print(\"%2d: %6.2f\" % (i+1, examAvg))" 345 | ] 346 | }, 347 | { 348 | "cell_type": "markdown", 349 | "metadata": {}, 350 | "source": [ 351 | "## The Matrix Abstract Data Type\n", 352 | "\n", 353 | "**Matrix is an *m x n* rectangular grid or table of numerical values divided into m rows and n columns.**\n", 354 | "Matrices are important tool in arease such as linear algebra and computer graphics, are used in a number of applications, including representing and solving systems of linear equations. \n", 355 | "\n", 356 | "### Matrix Operations\n", 357 | "\n", 358 | "A number of operations can be performed on matrices. \n", 359 | "\n", 360 | "**Matrix Addition**\n", 361 | "\n", 362 | "![Matrix Addition](http://3.bp.blogspot.com/-B4hSv-0sMCQ/TiMZZ2AeN5I/AAAAAAAABXg/oAZUcr-At_M/s1600/add.png)\n", 363 | "\n", 364 | "**Matrix Substraction**\n", 365 | "\n", 366 | "![Matrix Substraction](http://1.bp.blogspot.com/-iL0waODyMUU/TiMc9BxsSbI/AAAAAAAABXs/jLM3Ge7LlTk/s1600/diff.png)\n", 367 | "\n", 368 | "**Matrix Scaling**\n", 369 | "\n", 370 | "![Matrix Scaling](https://bakhmalamath9.files.wordpress.com/2014/05/matrices-multiplication.png)\n", 371 | "\n", 372 | "**Matrix Multiplication**\n", 373 | "\n", 374 | "![Matrix Multiplication](http://s3-ap-southeast-1.amazonaws.com/subscriber.images/maths/2016/10/01081108/matrix-.png)\n", 375 | "\n", 376 | "**Transpose of Matrix**\n", 377 | "\n", 378 | "![Transpose of Matrix](http://www.vuse.vanderbilt.edu/es140/images_mat/mlmatrix15.gif)\n", 379 | "\n", 380 | "\n", 381 | "You can see the Matrix ADT implementation in ```matrix_class.py``` script." 382 | ] 383 | }, 384 | { 385 | "cell_type": "code", 386 | "execution_count": null, 387 | "metadata": { 388 | "collapsed": true 389 | }, 390 | "outputs": [], 391 | "source": [] 392 | } 393 | ], 394 | "metadata": { 395 | "anaconda-cloud": {}, 396 | "kernelspec": { 397 | "display_name": "Python [default]", 398 | "language": "python", 399 | "name": "python2" 400 | }, 401 | "language_info": { 402 | "codemirror_mode": { 403 | "name": "ipython", 404 | "version": 2 405 | }, 406 | "file_extension": ".py", 407 | "mimetype": "text/x-python", 408 | "name": "python", 409 | "nbconvert_exporter": "python", 410 | "pygments_lexer": "ipython2", 411 | "version": "2.7.12" 412 | } 413 | }, 414 | "nbformat": 4, 415 | "nbformat_minor": 0 416 | } 417 | -------------------------------------------------------------------------------- /Chapter2/README.md: -------------------------------------------------------------------------------- 1 | ## Chapter 2: Arrays 2 | 3 | ### To Do List: 4 | 5 | - [x] Write chapter notes to ipynb 6 | - [x] Define and Implement Array1D ADT 7 | - [x] Write a small snippet to verify usage of Array1D ADT 8 | - [x] Define and Implement Array2D ADT 9 | - [x] Write a small snippet to verify usage of Array2D ADT 10 | - [x] Define and Implement Matrix ADT 11 | - [x] Write a small snippet to verify usage of Matrix ADT 12 | - [x] Define and Implement LifeGrid ADT 13 | - [x] Implement game of life 14 | - [x] Complete Matrix ADT by adding more methods 15 | - [x] Complete LifeGrid ADT by adding more methods 16 | - [x] Update gameoflife game play 17 | - [x] Define and Implement Vector ADT 18 | - [ ] Write a small snippet to verify usage of Vector ADT 19 | - [ ] Define and Implement GrayscaleImage ADT 20 | - [ ] Write a small snippet to verify usage of GrayscaleImage ADT 21 | - [ ] Write markdown document for Reversi Game and it's rules 22 | - [ ] Define and Implement Reversi Game Logic ADT 23 | - [ ] Implement text version of Reversi Game using Reversi Game Logic 24 | - [ ] Define and Implement logic ADT for game checkers (?) 25 | -------------------------------------------------------------------------------- /Chapter2/StudentGrades.txt: -------------------------------------------------------------------------------- 1 | 7 2 | 3 3 | 90 96 92 4 | 85 91 89 5 | 82 73 84 6 | 69 82 86 7 | 95 88 91 8 | 78 64 84 9 | 92 85 89 10 | -------------------------------------------------------------------------------- /Chapter2/array_class.py: -------------------------------------------------------------------------------- 1 | # Implements the Array ADT using array capabilities of the ctypes module. 2 | 3 | import ctypes 4 | 5 | class Array1D: 6 | # Creates an array with size. 7 | def __init__(self, size): 8 | assert size > 0, "Array size must be > 0" 9 | self._size = size 10 | # Create the array structure using the ctypes module 11 | PyArrayType = ctypes.py_object * size 12 | self._elements = PyArrayType() 13 | # Initialize each element. 14 | self.clear(None) 15 | 16 | # Returns the size of the array 17 | def __len__(self): 18 | return self._size 19 | 20 | # Gets the contents of the index element. 21 | def __getitem__(self, index): 22 | assert index >= 0 and index < len(self), "Array subscript out of range" 23 | return self._elements[index] 24 | 25 | # Puts the value in the array element at index position 26 | def __setitem__(self, index, value): 27 | assert index >= 0 and index < len(self), "Array subscript out of range" 28 | self._elements[index] = value 29 | 30 | # Clears the array by setting each element to the given value. 31 | def clear(self, value): 32 | for i in range(len(self)): 33 | self._elements[i] = value 34 | 35 | # Returns the array's iterator for traversing the elements. 36 | def __iter__(self): 37 | return _ArrayIterator(self._elements) 38 | 39 | 40 | # Implementation of the Array2D ADT using array of arrays 41 | class Array2D: 42 | # Creates a 2-D array of size numRows x numCols. 43 | def __init__(self, numRows, numCols): 44 | # Create a 1-D array to store an array reference for each row 45 | self._theRows = Array1D(numRows) 46 | 47 | # Create the 1-D arrays for each row of the 2-D array. 48 | for i in range(numRows): 49 | self._theRows[i] = Array1D(numCols) 50 | 51 | # Returns the number of rows in the 2-D array. 52 | def numRows(self): 53 | return len(self._theRows) 54 | 55 | # Returns the number of columns in the 2-D array 56 | def numCols(self): 57 | return len(self._theRows[0]) 58 | 59 | # Clears the array by setting every element to the given value. 60 | def clear(self, value): 61 | for row in range(self.numRows()): 62 | row.clear(value) 63 | 64 | # Gets the contents of the element at position [i, j] 65 | def __getitem__(self, ndxTuple): 66 | assert len(ndxTuple) == 2, "Invalid number of array subscripts." 67 | row = ndxTuple[0] 68 | col = ndxTuple[1] 69 | assert row >= 0 and row < self.numRows() \ 70 | and col >= 0 and col < self.numCols(), \ 71 | "Array subscript out of range." 72 | the1dArray = self._theRows[row] 73 | return the1dArray[col] 74 | 75 | # Sets the contents of the element at position [i, j] to value. 76 | def __setitem__(self, ndxTuple, value): 77 | assert len(ndxTuple) == 2, "Invalid number of array subscripts." 78 | row = ndxTuple[0] 79 | col = ndxTuple[1] 80 | assert row >= 0 and row < self.numRows() \ 81 | and col >= 0 and col < self.numCols(), \ 82 | "Array subscript out of range." 83 | 84 | the1dArray = self._theRows[row] 85 | the1dArray[col] = value 86 | 87 | # An iterator for Array ADT. 88 | class _ArrayIterator: 89 | def __init__(self, theArray): 90 | self._arrayRef = theArray 91 | self._curNdx = 0 92 | 93 | def __iter__(self): 94 | return self 95 | 96 | def __next__(self): 97 | if self._curNdx < len(self._arrayRef): 98 | entry = self._arrayRef[self._curNdx] 99 | self._curNdx += 1 100 | return entry 101 | else: 102 | raise StopIteration 103 | -------------------------------------------------------------------------------- /Chapter2/countOccurrences.py: -------------------------------------------------------------------------------- 1 | # Counting occurrences in text file using Array ADT 2 | from array_class import Array1D 3 | 4 | # Array theCounters created with size of 127 (ASCII characters) 5 | theCounters = Array1D(127) 6 | # theCounters elements initialized to 0 7 | theCounters.clear(0) 8 | 9 | # Open the text file for reading and extract each line from the file 10 | # and iterate over each character in the line. 11 | theFile = open('textfile.txt', 'r') 12 | for line in theFile: 13 | for letter in line: 14 | code = ord(letter) 15 | theCounters[code] += 1 16 | # Close the file 17 | theFile.close() 18 | 19 | # Print the results. The uppercase letters have ASCII values in the range 65..90 20 | # the lowercase letters are in the range 97..122. 21 | for i in range(26): 22 | print("%c - %4d %c - %4d" % (chr(65+i), theCounters[65+i], chr(97+i), theCounters[97+i])) 23 | -------------------------------------------------------------------------------- /Chapter2/examGrades.py: -------------------------------------------------------------------------------- 1 | # This script extracts exam grades from the text file and store them into 2-D array. 2 | 3 | from array_class import Array2D 4 | 5 | filename = "StudentGrades.txt" 6 | 7 | # Open the text file for reading. 8 | gradeFile = open(filename, "r") 9 | 10 | # Extract the first two values which indicate the size of the array. 11 | numStudents = int(gradeFile.readline()) 12 | numExams = int(gradeFile.readline()) 13 | 14 | # Create the 2-D array to store the grades. 15 | examGrades = Array2D(numStudents, numExams) 16 | 17 | # Extract the grades from the remaining lines. 18 | i = 0 19 | for student in gradeFile: 20 | grades = student.split() 21 | for j in range(numExams): 22 | examGrades[i,j] = int(grades[j]) 23 | i += 1 24 | 25 | # Close the text file. 26 | gradeFile.close() 27 | 28 | # Compute each student's average exam grade. 29 | for i in range(numStudents): 30 | # Tally the exam grades for the ith student. 31 | total = 0 32 | for j in range(numExams): 33 | total += examGrades[i,j] 34 | 35 | # Computer average for the ith student. 36 | examAvg = total / numExams 37 | print("%2d: %6.2f" % (i+1, examAvg)) 38 | -------------------------------------------------------------------------------- /Chapter2/gameOfLife.md: -------------------------------------------------------------------------------- 1 | ## The Game of Life 2 | 3 | The game of life devised by British mathematician John H. Conway, is a Soltaire-type game that is analogous with "the rise, fall and alternations of society of living organism." 4 | 5 | *Zero-player game :)* was introduced by Martin Gardner. After that the game of life became very popular and much observed to understand how complex systems or patterns can evolve from a simple set of rules. 6 | 7 | **Was an early example of a problem in the modern field of mathematics called cellular automata.** 8 | 9 | ### Rules of the Game 10 | 11 | The game uses an infinite-sized rectangular grid of cells in which each cell is either empty or occupied by an organism. The occupied cells are said to be alive, whereas the empty ones are dead. The game is played over a specific period of time with each turn creating a new *"generation"* based on the arrangement of live organisms in the current configuration. The status of a cell in the next generation is determined by applying the following four basic rules to each cell in the current configuration: 12 | 13 | 1. If a cell is alive and has either two or three live neighbors, the cell remains alive in the next generation. The neighbors are the eight cells immediately surrounding a cell: vertically, horizontally, and diagonally. 14 | 2. A living cell that has no live neighbors or a single live neighbor dies from isolation in the next generation 15 | 3. A living cell that has four or more live neighbors dies from overpopulation in the next generation. 16 | 4. A dead cell with exactly three live neighbors results in a birth and becomes alive in the next generation. All other dead cells remain dead in the next generation. 17 | 18 | 19 | 20 | So the game starts with an initial configuration supplied by the user. 21 | -------------------------------------------------------------------------------- /Chapter2/gameoflife.py: -------------------------------------------------------------------------------- 1 | # Main program that plays the game of life. 2 | import random 3 | from life import LifeGrid 4 | 5 | 6 | # # Define the initial configuration of live cells. 7 | # INIT_CONFIG = [(1,1), (1,2), (2,2), (3,2)] 8 | # 9 | # # Set the size of the grid. 10 | # GRID_WIDTH = 5 11 | # GRID_HEIGHT = 5 12 | # 13 | # # Indicate the number of generations. 14 | # NUM_GENS = 8 15 | 16 | def main(): 17 | # Construct the game grid and configure it. 18 | GRID_WIDTH = int(input("Enter width for the grid: ")) 19 | GRID_HEIGHT = int(input("Enter height for the grid: ")) 20 | NUM_GENS = int(input("Enter number of generations: ")) 21 | 22 | INIT_CONFIG = randomInitial(GRID_WIDTH, GRID_HEIGHT) 23 | print(INIT_CONFIG) 24 | grid = LifeGrid(GRID_WIDTH, GRID_HEIGHT) 25 | grid.configure(INIT_CONFIG) 26 | 27 | # Play the game 28 | draw(grid) 29 | for i in range(NUM_GENS): 30 | evolve(grid) 31 | draw(grid) 32 | 33 | # Generates the next generation of organisms. 34 | def evolve(grid): 35 | # List for storing the live cells of the next generation. 36 | liveCells = list() 37 | 38 | # Iterate over the elements of the grid. 39 | for i in range(grid.numRows()-1): 40 | # print i 41 | for j in range(grid.numCols()-1): 42 | # print j 43 | # Determine the number of live neighbors for this cells 44 | neighbors = grid.numLiveNeighbors(i,j) 45 | 46 | # Add the (i,j) tuple to liveCells if this cell contains 47 | # a live organism in the next generation 48 | if (neighbors == 2 and grid.isLiveCell(i,j)) or (neighbors ==3): 49 | liveCells.append((i,j)) 50 | 51 | # Reconfigure the grid using liveCells coord list. 52 | grid.configure(liveCells) 53 | 54 | # Prints a text-based representation of the game grid. 55 | def draw(grid): 56 | for row in range(grid.numRows()): 57 | for col in range(grid.numCols()): 58 | if grid.isLiveCell(row, col): 59 | print '@ ', 60 | else: 61 | print '. ', 62 | print '' 63 | print '' 64 | 65 | 66 | # Creates a random intial configuration 67 | def randomInitial(GRID_WIDTH, GRID_HEIGHT): 68 | """ 69 | numOfCells -> 4 Constant 70 | GRID_WIDTH -> input (int) 71 | GRID_HEIGHT -> input (int) 72 | 73 | """ 74 | numOfCells = 4 75 | new_config = [] 76 | for _ in range(numOfCells): 77 | temp_tuple = ( random.randrange(0, (GRID_WIDTH - 1)) , random.randrange( 0,(GRID_HEIGHT - 1)) ) 78 | new_config.append(temp_tuple) 79 | return new_config 80 | 81 | # Executes the main routine. 82 | if __name__ == '__main__': 83 | main() 84 | -------------------------------------------------------------------------------- /Chapter2/grayscale_class.py: -------------------------------------------------------------------------------- 1 | # Implementation of GrayscaleImage ADT 2 | from array_class import Array2D 3 | 4 | class GrayscaleImage: 5 | """ 6 | GrayscaleImage(nrows, ncols) -> Constructor 7 | width() -> return width 8 | height() -> return height 9 | clear() -> clears entire image 10 | getitem() -> returns the intensity level of given pixel 11 | setitem() -> Sets the intensity level of the given pixel 12 | 13 | 14 | """ 15 | # Creates a new instance that consists of nrows and ncols of pixels 16 | # each set to an initial value of 0. 17 | def __init__(self, nrows, nrols): 18 | self._theImage = Array2D(nrows, ncols) 19 | self._theImage.clear(0) 20 | # Initial value of 0 how? 21 | 22 | # Returns the width of the image. 23 | def width(self): 24 | return self._theImage.numCols() 25 | 26 | # Returns the height of the image. 27 | def height(self): 28 | return self._theImage.numRows() 29 | 30 | # Clears the entire image by setting each pixel to the given intensity value 31 | # The intensity value must be between [0:255] 32 | # Check This... 33 | def clear(self, value): 34 | return self._theImage.clear(value) 35 | 36 | # Returns the intensity level of the given pixel. 37 | # The pixel coordinates must be within the range. 38 | def __getitem__(self, row, col): 39 | assert row >= 0 and row < self.height() \ 40 | and col >= 0 and col < self.width(), \ 41 | "Array subscript out of range." 42 | return self._theImage[row, col] 43 | 44 | # Set's the intensity level of the given pixel to the given value. 45 | # The pixel coordinates must be within the valid range. 46 | # The intensity value must be between [0:255] 47 | def __setitem__(self, row, col, value): 48 | assert value >= 0 or value <= 255, "Intensity level out of range." 49 | assert row >= 0 and row < self.height() \ 50 | and col >= 0 and col < self.width(), \ 51 | "Array subscript out of range." 52 | self._theImage[row, col] = value 53 | -------------------------------------------------------------------------------- /Chapter2/life.py: -------------------------------------------------------------------------------- 1 | # Implements the LifeGrid ADT for use with the game of life. 2 | from array_class import Array2D 3 | 4 | class LifeGrid: 5 | # Defines constants to represent the cell states. 6 | DEAD_CELL = 0 7 | LIVE_CELL = 1 8 | 9 | # Creates the game grid and initializes the cells to dead. 10 | def __init__(self, numRows, numCols): 11 | # Allocate the 2-D array for the grid 12 | self._grid = Array2D(numRows, numCols) 13 | # Clear the grid and set all cell to dead 14 | self.configure(list()) 15 | 16 | # Returns the number of rows in the grid. 17 | def numRows(self): 18 | return self._grid.numRows() 19 | 20 | # Returns the number of columns in the grid. 21 | def numCols(self): 22 | return self._grid.numCols() 23 | 24 | # Configures the grid to contain the given live cells. 25 | def configure(self, coordList): 26 | # Clear the game grid. 27 | for i in range(self.numRows()): 28 | for j in range(self.numCols()): 29 | self.clearCell(i,j) 30 | 31 | # Set the indicated cells to be alive. 32 | for coord in coordList: 33 | self.setCell(coord[0], coord[1]) 34 | 35 | # Does the indicated cells to be alive 36 | def isLiveCell(self, row, col): 37 | return self._grid[row, col] == LifeGrid.LIVE_CELL 38 | 39 | # Clears the indicated cell by setting it to dead. 40 | def clearCell(self, row, col): 41 | self._grid[row, col] = LifeGrid.DEAD_CELL 42 | 43 | # Sets the indicated cell to be alive 44 | def setCell(self, row, col): 45 | self._grid[row, col] = LifeGrid.LIVE_CELL 46 | 47 | # Returns the number of live neighbors for the given cell. 48 | def numLiveNeighbors(self, row, col): 49 | result = 0 50 | for i in range(row - 1, col + 1): 51 | for j in range(col - 1, col + 2): 52 | if i > self._grid.numRows() or i < 0 or \ 53 | j > self._grid.numCols() or j < 0: 54 | counter = 0 55 | else: 56 | counter = self._grid[i, j] 57 | result += counter 58 | result -= self._grid[row, col] 59 | return result 60 | -------------------------------------------------------------------------------- /Chapter2/matrix_class.py: -------------------------------------------------------------------------------- 1 | # Implementation of the Matrix ADT using a 2-D array. 2 | from array_class import Array2D 3 | 4 | class Matrix: 5 | # Creates a matrix of size numRows x numCols initialized to 0. 6 | def __init__(self, numRows, numCols): 7 | self._theGrid = Array2D(numRows, numCols) 8 | self._theGrid.clear(0) 9 | 10 | # Returns the number of rows in the matrix. 11 | def numRows(self): 12 | return self._theGrid.numRows() 13 | 14 | # Returns the number of cols in the matrix 15 | def numCols(self): 16 | return self._theGrid.numCols() 17 | 18 | # Returns the value of element (i, j): x[i,j] 19 | def __getitem__(self, ndxTuple): 20 | return self._theGrid[ndxTuple[0], ndxTuple[1]] 21 | 22 | # Sets the value of element (i,j) to the value s: x[i,j] = s 23 | def __setitem__(self, ndxTuple, scalar): 24 | self._theGrid[ndxTuple[0], ndxTuple[1]] = scalar 25 | 26 | # Scales the matrix by the given scalar 27 | def scaleBy(self, scalar): 28 | for r in range(self.numRows()): 29 | for c in range(self.numCols()): 30 | self[r,c] += scalar 31 | 32 | # Creates and returns a new matrix that is the transpose of this matrix. 33 | def transpose(self): 34 | newMatrix = Matrix( self.numCols(), self,numRows() ) 35 | for i in range( self.numCols() ): 36 | for j in range( self.numRows() ): 37 | newMatrix[ i, j ] = self[ i, j ] 38 | return newMatrix 39 | 40 | # Creates and returns a new matrix that results from matrix addition. 41 | def __add__(self, rhsMatrix): 42 | assert rhsMatrix.numRows() == self.numRows() and \ 43 | rhsMatrix.numCols() == self.numCols(), \ 44 | "Matrix sizes not compatible for the add operation" 45 | 46 | # Create the new matrix 47 | newMatrix = Matrix(self.numRows(), self.numCols()) 48 | # Add the corresponding elements in the two matrices. 49 | for r in range(self.numRows()): 50 | for c in range(self.numCols()): 51 | newMatrix[r, c] = self[r, c] + rhsMatrix[r, c] 52 | 53 | return newMatrix 54 | 55 | # Creates and returns a new matrix that results from matrix substraction. 56 | def __sub__(self, rhsMatrix): 57 | assert rhsMatrix.numRows() == self.numRows() and \ 58 | rhsMatrix.numCols() == self.numCols(), \ 59 | "Matrix sizes not compatible for the add operation" 60 | 61 | # Create the new matrix 62 | newMatrix = Matrix(self.numRows(), self.numCols()) 63 | for i in range( self.numRows() ): 64 | for j in range( self.numCols() ): 65 | newMatrix[ i, j ] = self[ i, j ] - rhsMatrix[ i, j ] 66 | return newMatrix 67 | 68 | # Creates and returns a new matrix that results from matrix multiplication, 69 | def __mul__(self, rhsMatrix): 70 | assert rhsMatrix.numRows() == self.numRows() and \ 71 | rhsMatrix.numCols() == self.numCols(), \ 72 | "Matrix sizes not compatible for the add operation" 73 | 74 | # Create the new matrix 75 | newMatrix = Matrix(self.numRows(), self.numCols()) 76 | for i in range( self.numRows() ): 77 | for j in range( self.numRows() ): 78 | newMatrix[ i ,j ] = sum( [self[ i, x] * rhsMatrix[ x, j] for x in range( self.numCols())] ) 79 | return newMatrix 80 | -------------------------------------------------------------------------------- /Chapter2/vector_class.py: -------------------------------------------------------------------------------- 1 | # Vector ADT: Mutable sequence type that works like Python's Lists 2 | from array_class import Array1D 3 | 4 | class Vector: 5 | # Creates a new empty vector with and initial capacity of two elements 6 | def __init__(self): 7 | self._elements = Array1D(2) 8 | self._size = 0 9 | 10 | # Returns the number of items contained in the vector. 11 | def __len__(self): 12 | return self._size 13 | 14 | # Determines if the given item is contained in the vector. 15 | def __contains__(self, item): 16 | for i in range(len(self)): 17 | if self._elements[i] == item: 18 | return True 19 | return False 20 | 21 | # Return the item stored in the ndx element of the list. 22 | # The value of ndx must be within the valid range. 23 | def __getitem__(self, ndx): 24 | assert ndx >= 0 and ndx < len(self), 'Index out of Bound!' 25 | return self._elements[ndx] 26 | 27 | # Sets the element at position ndx to contain the given item 28 | # The value of ndx must be within the valid range, which 29 | # includes the first position past the last item 30 | def __setitem__(self, ndx, item): 31 | assert ndx >= 0 and ndx < len(self), 'Index out of Bound!' 32 | self._elements[ndx] = item 33 | 34 | # Adds the given item to the end of the list. 35 | def append(self, item): 36 | length = len(self._elements) 37 | if len(self) == length: 38 | self._expand(length+1) 39 | self._elements(len(self)) = item 40 | else: 41 | self._elements(len(self)) = item 42 | self._size += 1 43 | 44 | # Inserts the given item in the element at position ndx. 45 | # The items in the elements at and following the given positions are shifted 46 | # down to make room for the new item. 47 | # ndx must be within the valid range. 48 | def insert(self, ndx, item): 49 | assert ndx >= 0 and ndx < len(self), 'Index out of Bound!' 50 | length = len(self._elements) 51 | if len(self) == length: 52 | self._expand(length + 1) 53 | for i in range(len(self), ndx, -1): 54 | self._elements[i+1] = self._elements[i] 55 | self._elements[ndx] = item 56 | self._size =+ 1 57 | 58 | # Removes and returns the item from the element from the given ndx position. 59 | # The items in the elements at and following the given position are shifted 60 | # up to close the gap created by the remove item. 61 | # ndx must be within the valid range. 62 | def remove(self, ndx): 63 | assert ndx >= 0 and ndx < len(self), 'Index out of Bound!' 64 | for i in range(ndx, len(self)): 65 | self._elements[i] = self._elements[i+1] 66 | self._elements[ndx] = item 67 | self._size -= 1 68 | 69 | # Returns the index of the vector element containing the given item, 70 | # The item must be in the list. 71 | def indexOf(self, item): 72 | assert ndx >= 0 and ndx < len(self), 'Index out of Bound!' 73 | for i in range(len(self)): 74 | if self._elements[i] == item: 75 | return i 76 | 77 | # Extends this vector by appending the entire contents of the otherVector 78 | # to this vector 79 | def extend(self, otherVector): 80 | length = len(self._elements) 81 | if length > len(self) + len(otherVector): 82 | self._expand(len(self) + len(otherVector)) 83 | for item in otherVector: 84 | self.append(item) 85 | 86 | # Creates and returns a new vector that containse a subsequence of the items 87 | # in the vector between and including those indicated by the given from and 88 | # to positions. 89 | # Both the from and to positins must be within the valid range. 90 | def subvector(self, ndxTuple): 91 | assert type(ndxTuple) is tuple, 'Invalid Slicing Index!' 92 | _from, _to = ndxTuple[0], ndxTuple[1] 93 | newVector = Array1D(len(self)) 94 | for i in range(_from, _to): 95 | newVector[i - _from] = self [i] 96 | 97 | return newVector 98 | 99 | # Creates and returns and iterator that can be used to traverse the elements 100 | # of the vector. 101 | def __iter__(self): 102 | return _vectorIterator(self._elements) 103 | 104 | # Helper function to increase size of vector 105 | def _expand(self, targetLength): 106 | length = len(self._elements) 107 | if target > targetLength: 108 | newArray = Array1D(2 * length) 109 | for i in range(length): 110 | newArray[i] = self._elements[i] 111 | self._elements = newArray 112 | self._expand(targetLength) 113 | 114 | 115 | # An iterator for vector ADT 116 | class _vectorIterator: 117 | def __init__(self, Array): 118 | self._array = Array 119 | self._curNdx = 0 120 | 121 | def __iter__(self): 122 | return self 123 | 124 | def __next__(self): 125 | if self._curNdx < len(self._elements): 126 | item = self._array[self._curNdx] 127 | self._curNdx += 1 128 | return item 129 | else: 130 | raise StopIteration 131 | -------------------------------------------------------------------------------- /Chapter3/3-Sets_and_Maps.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Sets and Maps\n", 8 | "\n", 9 | "In this chapter we will look at the implementations of some of the Python's built-in abstract data types, sets and dictionaries, like we did in previous chapters. \n", 10 | "\n", 11 | "## Sets\n", 12 | "\n", 13 | "Unlike Bag ADT we defined in chapter 1, Set ADT -container- stores unique values and represents the same structure found in mathematics. \n", 14 | "\n", 15 | "### The Set Abstract Data Type\n", 16 | "\n", 17 | "We will implement the Set abstract data type using list for this chapter, later chapters will provide more implementations and we will evaluate them. \n", 18 | "\n", 19 | "You can see the implemented Set ADT in ```linearset.py```\n", 20 | "\n", 21 | "Now let's try our implementation, if it's working or not." 22 | ] 23 | }, 24 | { 25 | "cell_type": "code", 26 | "execution_count": 11, 27 | "metadata": { 28 | "collapsed": false 29 | }, 30 | "outputs": [], 31 | "source": [ 32 | "from linearset import Set\n", 33 | "\n", 34 | "smith = Set()\n", 35 | "smith.add(\"CSCI-112\")\n", 36 | "smith.add(\"MATH-121\")\n", 37 | "smith.add(\"HIST-340\")\n", 38 | "smith.add(\"ECON-101\")\n", 39 | "\n", 40 | "robert = Set()\n", 41 | "robert.add('POL-101')\n", 42 | "robert.add('ANTH-230')\n", 43 | "robert.add('CSCI-112')\n", 44 | "robert.add('ECON-101')" 45 | ] 46 | }, 47 | { 48 | "cell_type": "code", 49 | "execution_count": 12, 50 | "metadata": { 51 | "collapsed": false 52 | }, 53 | "outputs": [ 54 | { 55 | "ename": "NameError", 56 | "evalue": "global name 'element' is not defined", 57 | "output_type": "error", 58 | "traceback": [ 59 | "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", 60 | "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)", 61 | "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 2\u001b[0m \u001b[0;32mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"Smith and Robert are taking the same courses.\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 3\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 4\u001b[0;31m \u001b[0msameCourses\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0msmith\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mintersect\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mrobert\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;32mif\u001b[0m \u001b[0msameCourses\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0misEmpty\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[0;32mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"Smith and Robert are not taking any of classes together.\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", 62 | "\u001b[0;32m/Users/eneskemalergin/Desktop/github/Data_Structures_and_Algorithms/Chapter3/linearset.py\u001b[0m in \u001b[0;36mintersect\u001b[0;34m(self, setB)\u001b[0m\n\u001b[1;32m 54\u001b[0m \u001b[0mnewSet\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mSet\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 55\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0melement\u001b[0m \u001b[0;32min\u001b[0m \u001b[0msetB\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 56\u001b[0;31m \u001b[0;32mif\u001b[0m \u001b[0melement\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 57\u001b[0m \u001b[0mnewSet\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0madd\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0melement\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 58\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mnewSet\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", 63 | "\u001b[0;31mNameError\u001b[0m: global name 'element' is not defined" 64 | ] 65 | } 66 | ], 67 | "source": [ 68 | "if smith == robert:\n", 69 | " print(\"Smith and Robert are taking the same courses.\")\n", 70 | "else:\n", 71 | " sameCourses = smith.intersect(robert)\n", 72 | " if sameCourses.isEmpty():\n", 73 | " print(\"Smith and Robert are not taking any of classes together.\")\n", 74 | " else:\n", 75 | " print(\"Smith and Robert are taking some of the same courses\")\n", 76 | " for course in sameCourses:\n", 77 | " print(course)" 78 | ] 79 | }, 80 | { 81 | "cell_type": "markdown", 82 | "metadata": {}, 83 | "source": [ 84 | "## Maps\n", 85 | "\n", 86 | "Searching for data items based on unique key values is a very common application in computer science. ADT for this is often called **Map** or **Dictionaries**. It's is like database entities we store id/key for every value.\n", 87 | "\n", 88 | "In this section we define our own Map ADT and then provide an implementation using a list, later we will implement and evaluate the map using a variety of data structures. \n", 89 | "\n", 90 | "Map ADT implementation can be found ```linearmap.py```." 91 | ] 92 | }, 93 | { 94 | "cell_type": "markdown", 95 | "metadata": {}, 96 | "source": [ 97 | "## Multi-Dimensional Arrays\n", 98 | "\n", 99 | "Multi-dimensional arrays are with more than 2 dimensional arrays. In fact we can build as many dimensionsion as we want. \n", 100 | "\n", 101 | "### Data Organization\n", 102 | "\n", 103 | "**Array Storage:**\n", 104 | "\n", 105 | "A one-dimensional array is commonly used to physically store arrays of higher dimensions. How can the individual elements of the 2D table be stored in the one-dimensional structure while maintaining direct access to the individual table elements?\n", 106 | "\n", 107 | "- row-major order\n", 108 | "- column-major order\n", 109 | "\n", 110 | "In row-major order, the individual rows are stored sequentially, on at a time. \n", 111 | "\n", 112 | "![row-major order storage](http://eli.thegreenplace.net/images/2015/column-major-2D.png)" 113 | ] 114 | }, 115 | { 116 | "cell_type": "markdown", 117 | "metadata": {}, 118 | "source": [ 119 | "**Index Computation:**\n", 120 | "\n", 121 | "Since multi-dimensional arrays are created and managed by instrictions in the programming language, accessing an individual element must also be handled by the language. Given a 2D array of size mxn and using row-major ordering, an equation can be derived to computer this offset.\n", 122 | "\n", 123 | " index(i1, i2, .... , in) = i1 * f1 + i2 * f2 + ... + i(n-1) * f(n-1) + in * fn\\\n", 124 | " " 125 | ] 126 | }, 127 | { 128 | "cell_type": "code", 129 | "execution_count": 1, 130 | "metadata": { 131 | "collapsed": true 132 | }, 133 | "outputs": [], 134 | "source": [ 135 | "def func( *args):\n", 136 | " print(\"number of arguments: \", len(args))\n", 137 | " _sum = 0\n", 138 | " for value in args:\n", 139 | " _sum += value\n", 140 | " \n", 141 | " print (\"Sum of the arguments: \", _sum)" 142 | ] 143 | }, 144 | { 145 | "cell_type": "code", 146 | "execution_count": 2, 147 | "metadata": { 148 | "collapsed": false 149 | }, 150 | "outputs": [ 151 | { 152 | "name": "stdout", 153 | "output_type": "stream", 154 | "text": [ 155 | "('number of arguments: ', 1)\n", 156 | "('Sum of the arguments: ', 12)\n" 157 | ] 158 | } 159 | ], 160 | "source": [ 161 | "func(12)" 162 | ] 163 | }, 164 | { 165 | "cell_type": "code", 166 | "execution_count": 3, 167 | "metadata": { 168 | "collapsed": false 169 | }, 170 | "outputs": [ 171 | { 172 | "name": "stdout", 173 | "output_type": "stream", 174 | "text": [ 175 | "('number of arguments: ', 3)\n", 176 | "('Sum of the arguments: ', 15)\n" 177 | ] 178 | } 179 | ], 180 | "source": [ 181 | "func(5,8,2)" 182 | ] 183 | }, 184 | { 185 | "cell_type": "markdown", 186 | "metadata": {}, 187 | "source": [ 188 | "In the function above we used ```*args``` the asterisk next to argument name telss Python to accept any number of arguments and to combine them into a tuple." 189 | ] 190 | } 191 | ], 192 | "metadata": { 193 | "anaconda-cloud": {}, 194 | "kernelspec": { 195 | "display_name": "Python [default]", 196 | "language": "python", 197 | "name": "python2" 198 | }, 199 | "language_info": { 200 | "codemirror_mode": { 201 | "name": "ipython", 202 | "version": 2 203 | }, 204 | "file_extension": ".py", 205 | "mimetype": "text/x-python", 206 | "name": "python", 207 | "nbconvert_exporter": "python", 208 | "pygments_lexer": "ipython2", 209 | "version": "2.7.12" 210 | } 211 | }, 212 | "nbformat": 4, 213 | "nbformat_minor": 1 214 | } 215 | -------------------------------------------------------------------------------- /Chapter3/README.md: -------------------------------------------------------------------------------- 1 | ## Chapter 3: Sets and Maps 2 | 3 | ### To Do List: 4 | 5 | - [x] Finish writing notes in notebook 6 | - [x] Define and Implement Set ADT 7 | - [x] Write a small snippet to verify usage of Set ADT 8 | - [x] Define and Implement Map ADT 9 | - [x] Write a small snippet to verify usage of Map ADT 10 | - [x] Define and Implement MultiArray ADT 11 | - [x] Write a small snippet to verify usage of MultiArray ADT 12 | - [x] Implement Sales Report application 13 | - [x] Modify and make necessary addition to Set ADT 14 | - [ ] Modify and make necessary addition to Map ADT 15 | - [ ] Modify and make necessary addition to MultiArray ADT 16 | - [ ] Define and Implement TriangleArray ADT 17 | - [ ] Write a small snippet to verify usage of TriangleArray ADT 18 | - [ ] Define and Implement Set ADT again using Bag ADT 19 | -------------------------------------------------------------------------------- /Chapter3/array_class.py: -------------------------------------------------------------------------------- 1 | # Implements the Array ADT using array capabilities of the ctypes module. 2 | 3 | import ctypes 4 | 5 | class Array1D: 6 | # Creates an array with size. 7 | def __init__(self, size): 8 | assert size > 0, "Array size must be > 0" 9 | self._size = size 10 | # Create the array structure using the ctypes module 11 | PyArrayType = ctypes.py_object * size 12 | self._elements = PyArrayType() 13 | # Initialize each element. 14 | self.clear(None) 15 | 16 | # Returns the size of the array 17 | def __len__(self): 18 | return self._size 19 | 20 | # Gets the contents of the index element. 21 | def __getitem__(self, index): 22 | assert index >= 0 and index < len(self), "Array subscript out of range" 23 | return self._elements[index] 24 | 25 | # Puts the value in the array element at index position 26 | def __setitem__(self, index, value): 27 | assert index >= 0 and index < len(self), "Array subscript out of range" 28 | self._elements[index] = value 29 | 30 | # Clears the array by setting each element to the given value. 31 | def clear(self, value): 32 | for i in range(len(self)): 33 | self._elements[i] = value 34 | 35 | # Returns the array's iterator for traversing the elements. 36 | def __iter__(self): 37 | return _ArrayIterator(self._elements) 38 | 39 | 40 | # Implementation of the Array2D ADT using array of arrays 41 | class Array2D: 42 | # Creates a 2-D array of size numRows x numCols. 43 | def __init__(self, numRows, numCols): 44 | # Create a 1-D array to store an array reference for each row 45 | self._theRows = Array1D(numRows) 46 | 47 | # Create the 1-D arrays for each row of the 2-D array. 48 | for i in range(numRows): 49 | self._theRows[i] = Array1D(numCols) 50 | 51 | # Returns the number of rows in the 2-D array. 52 | def numRows(self): 53 | return len(self._theRows) 54 | 55 | # Returns the number of columns in the 2-D array 56 | def numCols(self): 57 | return len(self._theRows[0]) 58 | 59 | # Clears the array by setting every element to the given value. 60 | def clear(self, value): 61 | for row in range(self.numRows()): 62 | row.clear(value) 63 | 64 | # Gets the contents of the element at position [i, j] 65 | def __getitem__(self, ndxTuple): 66 | assert len(ndxTuple) == 2, "Invalid number of array subscripts." 67 | row = ndxTuple[0] 68 | col = ndxTuple[1] 69 | assert row >= 0 and row < self.numRows() \ 70 | and col >= 0 and col < self.numCols(), \ 71 | "Array subscript out of range." 72 | the1dArray = self._theRows[row] 73 | return the1dArray[col] 74 | 75 | # Sets the contents of the element at position [i, j] to value. 76 | def __setitem__(self, ndxTuple, value): 77 | assert len(ndxTuple) == 2, "Invalid number of array subscripts." 78 | row = ndxTuple[0] 79 | col = ndxTuple[1] 80 | assert row >= 0 and row < self.numRows() \ 81 | and col >= 0 and col < self.numCols(), \ 82 | "Array subscript out of range." 83 | 84 | the1dArray = self._theRows[row] 85 | the1dArray[col] = value 86 | 87 | # An iterator for Array ADT. 88 | class _ArrayIterator: 89 | def __init__(self, theArray): 90 | self._arrayRef = theArray 91 | self._curNdx = 0 92 | 93 | def __iter__(self): 94 | return self 95 | 96 | def __next__(self): 97 | if self._curNdx < len(self._arrayRef): 98 | entry = self._arrayRef[self._curNdx] 99 | self._curNdx += 1 100 | return entry 101 | else: 102 | raise StopIteration 103 | -------------------------------------------------------------------------------- /Chapter3/linearmap.py: -------------------------------------------------------------------------------- 1 | # Implementation of Map ADT using a single list. 2 | class Map: 3 | # Creates an empty map instance. 4 | def __init__(self): 5 | self._entryList = list() 6 | 7 | # Returns the number of entries in the map. 8 | def __len__(self): 9 | return len(self._entryList) 10 | 11 | # Determines if the map containes the given key. 12 | def __contains__(self, key): 13 | ndx = self._findPosition(key) 14 | return ndx is not None 15 | 16 | # Adds a new entry to the map if the key does exist. 17 | # Otherwise, the new value replaces the currect value associated with the key. 18 | def add(self, key, value): 19 | ndx = self._findPosition(key) 20 | if ndx is not None: # if the key was found 21 | self._entryList[ndx].value = value 22 | return False 23 | else: # Otherwisse add a new entry 24 | entry = _MapEntry(key, value) 25 | self._entryList.append(entry) 26 | return True 27 | 28 | # Returns the value associated with the key. 29 | def valueOf(self, key): 30 | ndx = self._findPosition(key) 31 | assert ndx is not None, "Invalid map key." 32 | self._entryList.pop(ndx) 33 | 34 | # Returns an iterator for traversing the keys in the map. 35 | def __iter__(self): 36 | return _MapIterator(self._entryList) 37 | 38 | # Helper method used to find the index position of a category. 39 | # If the key is not found, None is returned. 40 | def _findPosition(self, key): 41 | # Iterate through each entry in the list. 42 | for i in range(len(self)): 43 | # Is the key stored in the ith entry? 44 | if self._entryList[i].key == key: 45 | return i 46 | # When not found, return None. 47 | return None 48 | 49 | class _mapIterator: 50 | def __init__( self, aList ): 51 | self._list = aList 52 | self._curNdx = 0 53 | 54 | def __iter__( self ): 55 | return self 56 | 57 | def __next__( self ): 58 | if self._curNdx < len( self._list ): 59 | item = self._list[ self._curNdx ] 60 | self._curNdx += 1 61 | return item 62 | else: 63 | raise StopIteration 64 | 65 | # Storage class for holding the key/value pairs. 66 | class _MapEntry: 67 | def __init__(self, key, value): 68 | self.key = key 69 | self.value = value 70 | -------------------------------------------------------------------------------- /Chapter3/linearset.py: -------------------------------------------------------------------------------- 1 | # Implementation of the Set ADT container using a Python List. 2 | class Set: 3 | # Creates an empty set instance. 4 | def __init__(self, *initElements): 5 | if initElements != None: 6 | self._theElements = list(initElements) 7 | else: 8 | self._theElements = list() 9 | 10 | # Returns the number of items in the set. 11 | def __len__(self): 12 | return len(self._theElements) 13 | 14 | # Determines if an element is in the set. 15 | def __contains__(self, element): 16 | return element in self._theElements 17 | 18 | # Adds a new unique element to the set. 19 | def add(self, element): 20 | if element not in self: 21 | self._theElements.append(element) 22 | 23 | # Removes an element from the set. 24 | def remove (self, element): 25 | assert element in self, "The element must be in the set." 26 | self._theElements.remove(element) 27 | 28 | # Determines if two sets are equal. 29 | def __eq__(self, setB): 30 | if len(self) != len(setB): 31 | return False 32 | else: 33 | return self.isSubsetOf(setB) 34 | 35 | # Determines if this set is a subset of setB. 36 | def isSubsetOf(self, setB): 37 | for element in self._theElements: 38 | if element not in setB: 39 | return False 40 | return True 41 | 42 | # Creates a new set from the union if this set and setB 43 | def union(self, setB): 44 | newSet = Set() 45 | newSet._theElements.extend(self._theElements) 46 | for element in setB: 47 | if element not in self: 48 | newSet._theElements.append(element) 49 | return newSet 50 | 51 | # Creates a new set from the intersection: self set and setB 52 | def intersect(self, setB): 53 | newSet = Set() 54 | for element in setB: 55 | if element in self: 56 | newSet.add(element) 57 | return newSet 58 | 59 | 60 | # Creates a new set from the difference: self set and setB 61 | def difference(self, setB): 62 | newSet = Set() 63 | for element in setB: 64 | if element not in self: 65 | newSet.add(element) 66 | return newSet 67 | 68 | # Returns an itertor for traversing the list of items. 69 | def __iter__(self): 70 | return _SetIterator(self._theElements) 71 | 72 | # Returns the set as a string representation 73 | def __str__(self): 74 | return "Set: ", self._theElements 75 | 76 | # Addition operation method which uses union() method 77 | def __add__(self, setB): 78 | return self.union(setB) 79 | 80 | # Multiplication operation method which uses intersect() method 81 | def __mul__(self, setB): 82 | return self.intersect(setB) 83 | 84 | # Subtraction operation method which uses difference() method 85 | def __sub__(self, setB): 86 | return self.difference(setB) 87 | 88 | # comparison operation method which uses isSubsetOf() method 89 | def __lt__(self, setB): 90 | return self.isSubsetOf(setB) 91 | 92 | 93 | # Iterator class for set __iter__ 94 | class _SetIterator: 95 | def __init__( self, theList ): 96 | self._setItems = theList 97 | self._curNdx = 0 98 | 99 | def __iter__( self ): 100 | return self 101 | 102 | def next( self ): 103 | if self._curNdx < len( self._setItems ): 104 | item = self._setItems[ self._curNdx ] 105 | self._curNdx += 1 106 | return item 107 | else: 108 | raise StopIteration 109 | -------------------------------------------------------------------------------- /Chapter3/multiArray_class.py: -------------------------------------------------------------------------------- 1 | # Implementation of the MultiArray ADT using 1-D Array 2 | from array_class import Array1D 3 | 4 | class MultiArray: 5 | # Creates a multi-dimensional array. 6 | def __init__(self, *dimensions): 7 | assert len(dimensions) > 1, "The array must have 2 or more dimensions." 8 | # The variable argument tuple contains the dim sizes. 9 | self._dims = dimensions 10 | 11 | # Compute the total number of elements in the array. 12 | size = 1 13 | for d in dimensions: 14 | assert d > 0, "Dimensions must be > 0." 15 | size *= d 16 | 17 | # Create the 1-D array to store the elements. 18 | self._elements = Array1D(size) 19 | # Create a 1-D array to store the equation factors. 20 | self._factors = Array1D(len(dimensions)) 21 | self._computeFactors() 22 | 23 | # Returns the number of dimensions in the array. 24 | def numDims(self): 25 | return len(self._dims) 26 | 27 | # Returns the length of the given dimension. 28 | def length(self, dim): 29 | assert dim >= and dim < len(self._dims), "Dimension component out of range." 30 | return self._dims[dim - 1] 31 | 32 | # Clears the array by setting all elements to the given value. 33 | def clear(self, value): 34 | self._elements.clear(value) 35 | 36 | # Returns the contents of element (i_1, i_2, ..., i_n). 37 | def __getitem__(self, ndxTuple): 38 | assert len(ndxTuple) == self.numDims(), "Invalid # of array subscripts." 39 | index = self._computeIndex(ndxTuple) 40 | assert index is not None, "Array subscript out of range." 41 | return self._elements[index] 42 | 43 | # Sets the contents of element (i_1, i_2, ..., i_n). 44 | def __setitem__(self, ndxTuple, value): 45 | assert len(ndxTuple) == self.numDims(), "Invalid # of array subscripts." 46 | index = self._computeIndex(ndxTuple) 47 | assert index is not None, "Array subscript out of range." 48 | self._elements[index] = value 49 | 50 | # Computes the 1-D array offset for element (i_1, i_2, ..., i_n) 51 | # using the equation i_1 * f_1 + i_2 * f_2 + ... + i_n * f_n 52 | def _computeIndex(self, index): 53 | offset = 0 54 | for j in range(len(idx)): 55 | # Make sure the index components are within the legal range. 56 | if idx[j] < 0 || inx[j] >= self._dims[j]: 57 | return None 58 | else: 59 | offset += idx[j] * self._factors[j] 60 | return offset 61 | 62 | # Computes the factor values used in the index equation. 63 | def _computeFactors(self): 64 | for j in range(len(dim)): 65 | for d in dim[j+1:]: 66 | print d 67 | self[j] *= d 68 | return self 69 | -------------------------------------------------------------------------------- /Chapter3/salesReport.py: -------------------------------------------------------------------------------- 1 | # Application Sales Report 2 | # Need a txt file as an input 3 | 4 | from multiArray_class import MultiArray 5 | from array_class import Array1D 6 | 7 | # Main Function 8 | def main(): 9 | salesData = MultiArray(8, 100, 12) 10 | print("Continue to add things") 11 | 12 | 13 | # Compute the total sales of all items for all months in a given store. 14 | def totalSalesByStore(salesData, store): 15 | # Substract 1 from the store number since the array indices are 1 less 16 | # than the given store number 17 | s = store - 1 18 | 19 | # Accumulate the total sales for the given store. 20 | total = 0.0 21 | 22 | # Iterate over item. 23 | for i in range(salesData.length(2)): 24 | # Iterate over each month of the i item. 25 | from m in range(salesData.length(3)): 26 | total += salesData[s, i, m] 27 | 28 | return total 29 | 30 | 31 | # Compute the total sales of all items in all stores for a given month. 32 | def totalSalesByMonth(salesData, month): 33 | # The month number must be offset by 1. 34 | m = month - 1 35 | # Accumulate the total sales fro the given month. 36 | total = 0.0 37 | 38 | # Iterate over each stores. 39 | for s in range(salesData.length(1)): 40 | # Iterate over each item of the s stores 41 | for i in range(salesData.length(2)): 42 | total += salesData[s, i, m] 43 | 44 | return total 45 | 46 | 47 | # Compute the total sales of a single item in all stores over all months 48 | def totalSalesByItem(salesData, item): 49 | # The item number must be offset by 1. 50 | i = item - 1 51 | # Accumulate the total sales for the given item 52 | total = 0.0 53 | 54 | # Iterate over each store. 55 | for s in range(salesData.length(1)): 56 | # Iterate over each month of the s store. 57 | for m in range(salesData.length(3)): 58 | total += salesData[s, i, m] 59 | 60 | return total 61 | 62 | 63 | # Compute the total sales per month for a given store. A 1D array is returned 64 | # that contains the totals for each month. 65 | def totalSalesPerMonth(salesData, store): 66 | # The store number must be offset by 1. 67 | s = store - 1 68 | 69 | # The totals will be returned in a 1D array 70 | totals = Array1D(12) 71 | 72 | # Iterate over the sales of each month. 73 | for m in range(salesData.length(3)): 74 | _sum = 0.0 75 | 76 | # Iterate over the sales of each item sold during the m month. 77 | for i in range(salesData.length(2)): 78 | _sum += salesData[s, i, m] 79 | 80 | # Store result in the corresponding month of the totals array. 81 | totals[m] = _sum 82 | 83 | # Return the 1D array. 84 | return totals 85 | -------------------------------------------------------------------------------- /Chapter3/triangleArray_class.py: -------------------------------------------------------------------------------- 1 | # Implementation of triangle array ADT using Array1D 2 | 3 | class TriangleArray: 4 | 5 | # Construct 6 | def __init__(self, rowNums): 7 | pass 8 | 9 | -------------------------------------------------------------------------------- /Chapter4/4-Algorithm_Analysis.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Algorithm Analysis\n", 8 | "\n", 9 | "We can solve a problem with different solutions, but which one is better/best solution? We can answer this question by measuing the execution time, measuring the memory usage, and so on...\n", 10 | "\n", 11 | "---\n", 12 | "\n", 13 | "## Complexity Analysis\n", 14 | "\n", 15 | "To determine the efficiency of an alforithm, we can examine the solution itself and measure those aspects of the algorithms that most critically affect its execution time. \n", 16 | "\n", 17 | "Computing the sum of each rown of an nxn matrix and overal sum of the entire matrix:\n", 18 | "\n", 19 | "```Python\n", 20 | "# Version 1\n", 21 | "totalSum = 0\n", 22 | "for i in range(n):\n", 23 | " rowSum[i] = 0\n", 24 | " for j in range(n):\n", 25 | " rowSum[i] += matrix[i,j]\n", 26 | " totalSum += matrix[i, j]\n", 27 | "```\n", 28 | "\n", 29 | "2 nested loops and 2 addition operation takes ```2*(n**2)``` steps to complete. \n", 30 | "\n", 31 | "```Python \n", 32 | "# Version 2\n", 33 | "totalSum = 0\n", 34 | "for i in range(n):\n", 35 | " rowSum[i] = 0\n", 36 | " for j in range(n):\n", 37 | " rowSum[i] += matrix[i,j]\n", 38 | " totalSum += rowSum[i]\n", 39 | " \n", 40 | "```\n", 41 | "\n", 42 | "This time we took one of the inner loop's addition operation to outter loop which makes ```n*(n+1)``` \n", 43 | "\n", 44 | "Version 2 will be slightly faster however difference in the execution times will not be significant. \n", 45 | "\n", 46 | "### Big-O Notation\n", 47 | "\n", 48 | "In big notation we don't include constants or scalar multiplications, like in the example above version 1 and version 2 has ```O(n**2)``` complexity.\n", 49 | "\n", 50 | "\n", 51 | "### Evaluating Python Code\n", 52 | "\n", 53 | "**Linear Time O(n) Examples**\n", 54 | "\n", 55 | "Following example function run in O(n) times" 56 | ] 57 | }, 58 | { 59 | "cell_type": "code", 60 | "execution_count": 3, 61 | "metadata": { 62 | "collapsed": false 63 | }, 64 | "outputs": [ 65 | { 66 | "name": "stdout", 67 | "output_type": "stream", 68 | "text": [ 69 | "45\n" 70 | ] 71 | } 72 | ], 73 | "source": [ 74 | "def ex1(n):\n", 75 | " total = 0 \n", 76 | " for i in range(n):\n", 77 | " total += i\n", 78 | " return total\n", 79 | "\n", 80 | "print ex1(10)" 81 | ] 82 | }, 83 | { 84 | "cell_type": "markdown", 85 | "metadata": {}, 86 | "source": [ 87 | "Function ex2 runs in ```O(n) = O(n+n)```" 88 | ] 89 | }, 90 | { 91 | "cell_type": "code", 92 | "execution_count": 4, 93 | "metadata": { 94 | "collapsed": false 95 | }, 96 | "outputs": [ 97 | { 98 | "name": "stdout", 99 | "output_type": "stream", 100 | "text": [ 101 | "20\n" 102 | ] 103 | } 104 | ], 105 | "source": [ 106 | "def ex2(n):\n", 107 | " count = 0\n", 108 | " for i in range(n):\n", 109 | " count += 1\n", 110 | " for j in range(n):\n", 111 | " count += 1\n", 112 | " return count\n", 113 | "\n", 114 | "print ex2(10)" 115 | ] 116 | }, 117 | { 118 | "cell_type": "markdown", 119 | "metadata": {}, 120 | "source": [ 121 | "**Quadratic Time Examples**\n" 122 | ] 123 | }, 124 | { 125 | "cell_type": "code", 126 | "execution_count": 5, 127 | "metadata": { 128 | "collapsed": true 129 | }, 130 | "outputs": [], 131 | "source": [ 132 | "def ex3(n):\n", 133 | " count = 0\n", 134 | " for i in range(n):\n", 135 | " for j in range(n):\n", 136 | " count += 1\n", 137 | " return count\n" 138 | ] 139 | }, 140 | { 141 | "cell_type": "markdown", 142 | "metadata": {}, 143 | "source": [ 144 | "**Logarithmic Time Examples**\n", 145 | "\n", 146 | "The next example contains a single loop, but notice the change to the modification step. Instead of incrementing (or decrementing) by one, it cuts the loop variable in half each time through the loop" 147 | ] 148 | }, 149 | { 150 | "cell_type": "code", 151 | "execution_count": 6, 152 | "metadata": { 153 | "collapsed": true 154 | }, 155 | "outputs": [], 156 | "source": [ 157 | "def ex6(n): ## O(log n)\n", 158 | " count = 0\n", 159 | " i = n\n", 160 | " while i >= 1:\n", 161 | " count += 1\n", 162 | " i = i // 2\n", 163 | " return count" 164 | ] 165 | }, 166 | { 167 | "cell_type": "code", 168 | "execution_count": 7, 169 | "metadata": { 170 | "collapsed": true 171 | }, 172 | "outputs": [], 173 | "source": [ 174 | "def ex7(n): ## O(n*log n)\n", 175 | " count = 0\n", 176 | " for i in range(n):\n", 177 | " count += ex6(n)\n", 178 | " return count" 179 | ] 180 | }, 181 | { 182 | "cell_type": "markdown", 183 | "metadata": {}, 184 | "source": [ 185 | "**Different Cases**\n", 186 | "\n", 187 | "These different case algorithms can be evaluated for their best, worst, and average cases.\n", 188 | "\n", 189 | "Example traverses a list containing integer values to find the position of the first negative value. Note that for problem below, the input is the collection of n values contained in the list." 190 | ] 191 | }, 192 | { 193 | "cell_type": "code", 194 | "execution_count": 9, 195 | "metadata": { 196 | "collapsed": false 197 | }, 198 | "outputs": [], 199 | "source": [ 200 | "def findNeq(intList):\n", 201 | " n = len(intList)\n", 202 | " for i in range(n):\n", 203 | " if intList[i] < 0:\n", 204 | " return i\n", 205 | " return None" 206 | ] 207 | }, 208 | { 209 | "cell_type": "markdown", 210 | "metadata": {}, 211 | "source": [ 212 | "- Worst case of this algorithm would be not to have negative value, since there is none algorithm will go till the end. O(N)\n", 213 | "- Best case of this algorith would be to find negative value in index 0, which for loop won't iterate further. O(1)\n", 214 | "- Average case is requires n/2 times which maskes O(n) again. \n", 215 | "\n", 216 | "In general, we are more interested in the worst case time-complexity of an algorithm as it provides an upper bound over all possible inputs. \n", 217 | "\n", 218 | "\n", 219 | "## Evaluating the Python List\n", 220 | "\n", 221 | "We used Python list to build our own abstract data types, so it would be beneficial for us to learn Python Lists algorithmic efficiency, then we could analyse our adt's efficiency.\n", 222 | "\n", 223 | "List Operation | Worst Case \n", 224 | ":-- | :--: \n", 225 | "v = list() | O(1)\n", 226 | "v = [0] * n | O(n)\n", 227 | "v[i] = x | O(1)\n", 228 | "v.append(x) | O(n)\n", 229 | "v.extend(w) | O(n)\n", 230 | "v.insert(x) | O(n)\n", 231 | "v.pop() | O(n)\n", 232 | "traversal | O(n)\n" 233 | ] 234 | }, 235 | { 236 | "cell_type": "markdown", 237 | "metadata": {}, 238 | "source": [ 239 | "**List Traversal**\n", 240 | "\n", 241 | "A sequence traversal accesses the individual items, one after the other, in order to perform some operation on every item. \n", 242 | "\n", 243 | "```Python \n", 244 | "_sum = 0\n", 245 | "for value in valueList:\n", 246 | " _sum += value\n", 247 | "\n", 248 | "# Same as \n", 249 | "_sum = 0\n", 250 | "for i in range(len(valueList)):\n", 251 | " _sum += valueList[i]\n", 252 | "```\n", 253 | "\n", 254 | "list containse n elements and loop iterates n times which makes our runtime O(n)" 255 | ] 256 | } 257 | ], 258 | "metadata": { 259 | "anaconda-cloud": {}, 260 | "kernelspec": { 261 | "display_name": "Python [default]", 262 | "language": "python", 263 | "name": "python2" 264 | }, 265 | "language_info": { 266 | "codemirror_mode": { 267 | "name": "ipython", 268 | "version": 2 269 | }, 270 | "file_extension": ".py", 271 | "mimetype": "text/x-python", 272 | "name": "python", 273 | "nbconvert_exporter": "python", 274 | "pygments_lexer": "ipython2", 275 | "version": "2.7.12" 276 | } 277 | }, 278 | "nbformat": 4, 279 | "nbformat_minor": 1 280 | } 281 | -------------------------------------------------------------------------------- /Chapter4/README.md: -------------------------------------------------------------------------------- 1 | ## Chapter 4: Algorithm Analysis 2 | 3 | ### To Do List: 4 | 5 | - [x] Finish writing notes in ipynb 6 | - [x] Evaluate Set ADT from Chapter 3 7 | - [x] Define and Implement SparseMatrix ADT 8 | - [ ] Modify and make necessary addition to SparseMatrix ADT 9 | - [ ] Add Python operators for SparseMatrix ADT (```__add__(rhsMatrix)```) 10 | - [ ] Implement SparseLifeGrid ADT from given definition in the chapter 11 | - [ ] Implement a new version of the gameoflife.py program that uses SparseLifeGrid 12 | - [ ] Define and Implement ColorImage ADT using RGBColor ADT (Array2D) 13 | - [ ] ... 14 | -------------------------------------------------------------------------------- /Chapter4/sparsematrix.py: -------------------------------------------------------------------------------- 1 | # Implementation of the Sparese Matrix ADT using a list. 2 | 3 | class SparseMatrix: 4 | # Create a sparse matrix of size numRowsx numCols initialized to 0. 5 | def __init__(self, numRows, numCols): 6 | self._numRows = numRows 7 | self._numCols = numCols 8 | self._elementList = list() 9 | 10 | # Return the number of rows in the matrix. 11 | def numRows(self): 12 | return self._numRows 13 | 14 | # Return the number of cols in the matrix. 15 | def colRows(self): 16 | return self._numCols 17 | 18 | # Return the value of element (i, j): x[i,j] 19 | def __getitem__(self, ndxTuple): 20 | pass 21 | 22 | # Set the value of element (i,j) to the value s: x[i,j] = s 23 | def __setitem__(self, ndxTuple, scalar): 24 | ndx = self._findPositions(ndxTuple[0], ndxTuple[1]) 25 | # If item found in the list 26 | if ndx is not None: 27 | if scalar != 0.0: 28 | self._elementList[ndx].value = scalar 29 | else: 30 | self._elementList.pop(ndx) 31 | # If the element is zero and not in the list 32 | else: 33 | if scalar != 0.0: 34 | element = _MatrixElement(ndxTuple[0], ndxTuple[1], scalar) 35 | self._elementList.appen(element) 36 | 37 | # Scale the matrix by the given scalar. 38 | def scaleBy(self, scalar): 39 | for element in self._elementList: 40 | element.value *= scalar 41 | 42 | # Add method with efficiency O(n**2) 43 | # def __add__(self, rhsMatrix): 44 | # for r in range(self.numRows()): 45 | # for c in range(self.numCols()): 46 | # newMatrix[r,c] = self[r,c] + rhsMatrix[r,c] 47 | # return newMatrix 48 | 49 | # Add method with O(n) 50 | def add(self, rhsMatrix): 51 | assert rhsMatrix.numRows() == self.numRows() and \ 52 | rhsMatrix.numCols() == self.numCols(), \ 53 | "Matrix sizes not compatible for the add operation." 54 | 55 | # Create the new matrix. 56 | newMatrix = SparseMatrix(self.numRows(), self.numCols()) 57 | 58 | # Duplicate the lhs matrix. The elements are mutable, thus we must 59 | # create new objects and not simply copy the references. 60 | for element in self._elementList: 61 | dupElement = _MatrixElement(element.row, element.col, element.value) 62 | newMatrix._elementList.append(dupElement) 63 | 64 | # Iterate through each non-zero element of the rhsMatrix 65 | for element in rhsMatrix._elementList: 66 | # Get the value of the corresponding element in the new matrix. 67 | value = newMatrix[element.row, element.col] 68 | value += element.value 69 | # Store the new value back to the new matrix. 70 | newMatrix[element.row, element.col] = value 71 | 72 | return newMatrix 73 | 74 | def transpose(self): 75 | pass 76 | 77 | def substract(self, rhsMatrix): 78 | pass 79 | 80 | def multiply(self, rhsMatrix): 81 | pass 82 | 83 | def __add__(self, rhsMatrix): 84 | pass 85 | 86 | def __sub__(self, rhsMatrix): 87 | pass 88 | 89 | def __mul__(self, rhsMatrix): 90 | pass 91 | 92 | 93 | 94 | 95 | 96 | # Helper method used to find a specific matrix element (row, col) in the 97 | # list of non-zero entries. None is returned if the element is not found. 98 | def _findPositions(self, row, col): 99 | n = len(self._elementList) 100 | for i in range(n): 101 | if row == self._elementList[i].row and \ 102 | col == self._elementList[i].col: 103 | return i # Return the index of the element if found. 104 | return None # Return none when the element is zero. 105 | 106 | 107 | # Storage class for holding the non-zero matrix elements. 108 | class _MatrixElement: 109 | def __init__(self, row, col, value): 110 | self.row = row 111 | self.col = col 112 | self.value = value 113 | -------------------------------------------------------------------------------- /Chapter5/5-Searching_and_Sorting.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Searching and Sorting\n", 8 | "\n", 9 | "\n", 10 | "## Searching\n", 11 | "\n", 12 | "Process of selecting information from a collection based on specific criteria. \n", 13 | "\n", 14 | "### Linear Search\n", 15 | "\n", 16 | "Simplest approach to searching problem is sequential or linear search. It's basically starting from first element and goint one by one untill the item found. \n", 17 | "\n", 18 | "In Python, a specific item can be found with ```in``` operator" 19 | ] 20 | }, 21 | { 22 | "cell_type": "code", 23 | "execution_count": 6, 24 | "metadata": { 25 | "collapsed": false 26 | }, 27 | "outputs": [ 28 | { 29 | "name": "stdout", 30 | "output_type": "stream", 31 | "text": [ 32 | "The key is not in the array.\n" 33 | ] 34 | } 35 | ], 36 | "source": [ 37 | "theArray = range(0,100)\n", 38 | "key = 101\n", 39 | "if key in theArray:\n", 40 | " print(\"The key is in the array.\")\n", 41 | "else:\n", 42 | " print(\"The key is not in the array.\")" 43 | ] 44 | }, 45 | { 46 | "cell_type": "markdown", 47 | "metadata": {}, 48 | "source": [ 49 | "Okay, using ```in``` operator gives us a great deal of simplicity, but we should know the behind the scenes of ```in``` operator. " 50 | ] 51 | }, 52 | { 53 | "cell_type": "code", 54 | "execution_count": 7, 55 | "metadata": { 56 | "collapsed": true 57 | }, 58 | "outputs": [], 59 | "source": [ 60 | "def linearSearch(theValues, target):\n", 61 | " n = len(theValues)\n", 62 | " for i in range(n):\n", 63 | " # If the target is in the ith element, return True\n", 64 | " if theValues[i] == target:\n", 65 | " return True\n", 66 | " # If not found, return False.\n", 67 | " return False\n", 68 | " " 69 | ] 70 | }, 71 | { 72 | "cell_type": "markdown", 73 | "metadata": {}, 74 | "source": [ 75 | "Finding a specific item from unsorted list resulted worst case time of O(n), since it has to go all the way to the end n iteration.\n", 76 | "\n", 77 | "What about if we search through Sorted sequence? Since we know that the list is sorted we might avoid going through the end, we can terminate it if we came to bigger number than our target. This produces the somewhat better version than the unsorted linear search but complexity is still the same.\n" 78 | ] 79 | }, 80 | { 81 | "cell_type": "code", 82 | "execution_count": 8, 83 | "metadata": { 84 | "collapsed": true 85 | }, 86 | "outputs": [], 87 | "source": [ 88 | "def sortedLinearSearch(theValues, item):\n", 89 | " n = len(theValues)\n", 90 | " for i in range(n):\n", 91 | " # If the target is found in the ith element, return True\n", 92 | " if theValues[i] == item:\n", 93 | " return True\n", 94 | " # If target is largers than the ith element, it's not in the sequence.\n", 95 | " elif theValues[i] > item:\n", 96 | " return False\n", 97 | " \n", 98 | " # The item is not in the sequence.\n", 99 | " return False" 100 | ] 101 | }, 102 | { 103 | "cell_type": "markdown", 104 | "metadata": {}, 105 | "source": [ 106 | "**Finding the Smallest Value**\n", 107 | "\n", 108 | "It is equivalen to ```min()``` function in Python. To be able to accomplish this we have to do linear search but this time we have to keep track of the smallest number. Complexity is O(n) again.\n" 109 | ] 110 | }, 111 | { 112 | "cell_type": "code", 113 | "execution_count": 9, 114 | "metadata": { 115 | "collapsed": true 116 | }, 117 | "outputs": [], 118 | "source": [ 119 | "def findSmallest(theValues):\n", 120 | " n = len(theValues)\n", 121 | " # Assume the first item is the smallest value\n", 122 | " smallest = theValues[0]\n", 123 | " # Determine if any other item in the sequence is smaller.\n", 124 | " for i in range(1,n):\n", 125 | " if theValues[i] < smallest:\n", 126 | " smallest = theValues[i]\n", 127 | " # Return the smallest found.\n", 128 | " return smallest" 129 | ] 130 | }, 131 | { 132 | "cell_type": "markdown", 133 | "metadata": {}, 134 | "source": [ 135 | "We can do the same thing for finding the maximum number, Python's ```max()``` implementation" 136 | ] 137 | }, 138 | { 139 | "cell_type": "code", 140 | "execution_count": 10, 141 | "metadata": { 142 | "collapsed": true 143 | }, 144 | "outputs": [], 145 | "source": [ 146 | "def findBiggest(theValues):\n", 147 | " n = len(theValues)\n", 148 | " # Assuming the first item is the biggest value\n", 149 | " biggest = theValues[0]\n", 150 | " # Determine if any other item in the sequence is bigger.\n", 151 | " for i in range(1, n):\n", 152 | " if theValues[i] > biggest:\n", 153 | " biggest = theValues[i]\n", 154 | " #Return the biggest found.\n", 155 | " return biggest" 156 | ] 157 | }, 158 | { 159 | "cell_type": "markdown", 160 | "metadata": {}, 161 | "source": [ 162 | "### The Binary Search\n", 163 | "\n", 164 | "![Binary Search](https://s3.amazonaws.com/learneroo-images/main/binarySearch.png)" 165 | ] 166 | }, 167 | { 168 | "cell_type": "code", 169 | "execution_count": 12, 170 | "metadata": { 171 | "collapsed": true 172 | }, 173 | "outputs": [], 174 | "source": [ 175 | "def binarySearch(theValues, target):\n", 176 | " # Start with the entire sequence of elements. 0:length\n", 177 | " low = 0 \n", 178 | " high = len(theValues - 1)\n", 179 | " \n", 180 | " # Repeatedly subdivide the sequence in half until the target is found.\n", 181 | " while low <= high:\n", 182 | " # Find the midpoint of the sequence.\n", 183 | " mid = (high + low) // 2\n", 184 | " # Does the midpoint contain the target?\n", 185 | " if theValues[mid] == target:\n", 186 | " return True\n", 187 | " # Or does the target precede the midpoint?\n", 188 | " elif target < theValues[mid]:\n", 189 | " high = mid - 1 # Update the upper bound\n", 190 | " \n", 191 | " # Or does it follow the midpoint\n", 192 | " else:\n", 193 | " low = mid + 1 # Update the lower bound\n", 194 | " \n", 195 | " # If the sequence cannot be subdivided further, we're done.\n", 196 | " return False" 197 | ] 198 | }, 199 | { 200 | "cell_type": "markdown", 201 | "metadata": {}, 202 | "source": [ 203 | "## Sorting \n", 204 | "\n", 205 | "Sorting is the process of ordering or arranging a collection of items.\n", 206 | "\n", 207 | "### Bubble Sort\n", 208 | "\n", 209 | "![Bubble Sort](http://www.algolist.net/img/sorts/bubble-sort-3.png)\n" 210 | ] 211 | }, 212 | { 213 | "cell_type": "code", 214 | "execution_count": 25, 215 | "metadata": { 216 | "collapsed": true 217 | }, 218 | "outputs": [], 219 | "source": [ 220 | "# Sorts a sequence in ascending order using the bubble sort algorith.\n", 221 | "def bubbleSort(seq):\n", 222 | " not_sorted = True\n", 223 | " n = len(seq)\n", 224 | " print \"At the beginning: \"\n", 225 | " print seq\n", 226 | " while not_sorted:\n", 227 | " # If following statements fail next statement will stop the loop\n", 228 | " not_sorted = False\n", 229 | " for i in range(n-1):\n", 230 | " if seq[i] <= seq[i+1]:\n", 231 | " continue;\n", 232 | " else:\n", 233 | " temp = seq[i]\n", 234 | " seq[i] = seq[i+1]\n", 235 | " seq[i+1] = temp\n", 236 | "\n", 237 | " not_sorted = True\n", 238 | " print seq\n", 239 | " return seq\n" 240 | ] 241 | }, 242 | { 243 | "cell_type": "code", 244 | "execution_count": 26, 245 | "metadata": { 246 | "collapsed": true 247 | }, 248 | "outputs": [], 249 | "source": [ 250 | "import random\n", 251 | "_list = random.sample(xrange(1, 101), 10)" 252 | ] 253 | }, 254 | { 255 | "cell_type": "code", 256 | "execution_count": 27, 257 | "metadata": { 258 | "collapsed": false 259 | }, 260 | "outputs": [ 261 | { 262 | "data": { 263 | "text/plain": [ 264 | "[38, 75, 7, 48, 34, 69, 32, 61, 71, 80]" 265 | ] 266 | }, 267 | "execution_count": 27, 268 | "metadata": {}, 269 | "output_type": "execute_result" 270 | } 271 | ], 272 | "source": [ 273 | "_list" 274 | ] 275 | }, 276 | { 277 | "cell_type": "code", 278 | "execution_count": 28, 279 | "metadata": { 280 | "collapsed": false 281 | }, 282 | "outputs": [ 283 | { 284 | "name": "stdout", 285 | "output_type": "stream", 286 | "text": [ 287 | "At the beginning: \n", 288 | "[38, 75, 7, 48, 34, 69, 32, 61, 71, 80]\n", 289 | "[38, 7, 75, 48, 34, 69, 32, 61, 71, 80]\n", 290 | "[38, 7, 48, 75, 34, 69, 32, 61, 71, 80]\n", 291 | "[38, 7, 48, 34, 75, 69, 32, 61, 71, 80]\n", 292 | "[38, 7, 48, 34, 69, 75, 32, 61, 71, 80]\n", 293 | "[38, 7, 48, 34, 69, 32, 75, 61, 71, 80]\n", 294 | "[38, 7, 48, 34, 69, 32, 61, 75, 71, 80]\n", 295 | "[38, 7, 48, 34, 69, 32, 61, 71, 75, 80]\n", 296 | "[7, 38, 48, 34, 69, 32, 61, 71, 75, 80]\n", 297 | "[7, 38, 34, 48, 69, 32, 61, 71, 75, 80]\n", 298 | "[7, 38, 34, 48, 32, 69, 61, 71, 75, 80]\n", 299 | "[7, 38, 34, 48, 32, 61, 69, 71, 75, 80]\n", 300 | "[7, 34, 38, 48, 32, 61, 69, 71, 75, 80]\n", 301 | "[7, 34, 38, 32, 48, 61, 69, 71, 75, 80]\n", 302 | "[7, 34, 32, 38, 48, 61, 69, 71, 75, 80]\n", 303 | "[7, 32, 34, 38, 48, 61, 69, 71, 75, 80]\n" 304 | ] 305 | }, 306 | { 307 | "data": { 308 | "text/plain": [ 309 | "[7, 32, 34, 38, 48, 61, 69, 71, 75, 80]" 310 | ] 311 | }, 312 | "execution_count": 28, 313 | "metadata": {}, 314 | "output_type": "execute_result" 315 | } 316 | ], 317 | "source": [ 318 | "bubbleSort(_list)" 319 | ] 320 | }, 321 | { 322 | "cell_type": "markdown", 323 | "metadata": {}, 324 | "source": [ 325 | "### Selection Sort\n", 326 | "\n", 327 | "![Selection Sort](http://i.stack.imgur.com/1SKb2.jpg)" 328 | ] 329 | }, 330 | { 331 | "cell_type": "code", 332 | "execution_count": 31, 333 | "metadata": { 334 | "collapsed": true 335 | }, 336 | "outputs": [], 337 | "source": [ 338 | "# Sorts a sequence in ascending order using the selection sort algorithm\n", 339 | "def selectionSort(theSeq):\n", 340 | " n = len(theSeq)\n", 341 | " for i in range(n-1):\n", 342 | " # Assume the ith element is the smallest.\n", 343 | " smallNdx = i\n", 344 | " for j in range(i+1, n):\n", 345 | " if theSeq[j] < theSeq[smallNdx]:\n", 346 | " smallNdx = j\n", 347 | " \n", 348 | " # Swap the ith value and smallNdx value only if the smallest value is \n", 349 | " # not really in its proper position. Some implementations omit testing \n", 350 | " # the condition and always swap the two values.\n", 351 | " if smallNdx != i:\n", 352 | " tmp = theSeq[i]\n", 353 | " theSeq[i] = theSeq[smallNdx]\n", 354 | " theSeq[smallNdx] = tmp\n", 355 | "\n", 356 | " return theSeq" 357 | ] 358 | }, 359 | { 360 | "cell_type": "code", 361 | "execution_count": 34, 362 | "metadata": { 363 | "collapsed": true 364 | }, 365 | "outputs": [], 366 | "source": [ 367 | "import random\n", 368 | "_list = random.sample(xrange(1, 101), 10)" 369 | ] 370 | }, 371 | { 372 | "cell_type": "code", 373 | "execution_count": 35, 374 | "metadata": { 375 | "collapsed": false 376 | }, 377 | "outputs": [ 378 | { 379 | "name": "stdout", 380 | "output_type": "stream", 381 | "text": [ 382 | "[41, 17, 12, 61, 56, 28, 38, 51, 19, 82]\n" 383 | ] 384 | } 385 | ], 386 | "source": [ 387 | "print _list" 388 | ] 389 | }, 390 | { 391 | "cell_type": "code", 392 | "execution_count": 36, 393 | "metadata": { 394 | "collapsed": false 395 | }, 396 | "outputs": [ 397 | { 398 | "data": { 399 | "text/plain": [ 400 | "[12, 17, 19, 28, 38, 41, 51, 56, 61, 82]" 401 | ] 402 | }, 403 | "execution_count": 36, 404 | "metadata": {}, 405 | "output_type": "execute_result" 406 | } 407 | ], 408 | "source": [ 409 | "selectionSort(_list)" 410 | ] 411 | }, 412 | { 413 | "cell_type": "markdown", 414 | "metadata": {}, 415 | "source": [ 416 | "### Insertion Sort\n", 417 | "\n", 418 | "![Insertion Sort](http://freefeast.info/wp-content/uploads//2013/01/Insertion-Sort-Model11.jpg)" 419 | ] 420 | }, 421 | { 422 | "cell_type": "code", 423 | "execution_count": 37, 424 | "metadata": { 425 | "collapsed": true 426 | }, 427 | "outputs": [], 428 | "source": [ 429 | "# Sorts a sequence in ascending order using the insertion sort algorithm.\n", 430 | "def insertionSort(theSeq):\n", 431 | " n = len(theSeq)\n", 432 | " # Starts with the first item as the only sorted entry.\n", 433 | " for i in range(1, n):\n", 434 | " # Save the value to be positioned.\n", 435 | " value = theSeq[i]\n", 436 | " # Find the position where value fits in the ordered part of the list.\n", 437 | " pos = i\n", 438 | " while pos > 0 and value < theSeq[pos - 1]:\n", 439 | " # Shift the items to the rigth during search\n", 440 | " theSeq[pos] = theSeq[pos - 1]\n", 441 | " pos -= 1\n", 442 | " \n", 443 | " theSeq[pos] = value\n", 444 | " return theSeq" 445 | ] 446 | }, 447 | { 448 | "cell_type": "code", 449 | "execution_count": 38, 450 | "metadata": { 451 | "collapsed": true 452 | }, 453 | "outputs": [], 454 | "source": [ 455 | "import random\n", 456 | "_list = random.sample(xrange(1, 101), 10)" 457 | ] 458 | }, 459 | { 460 | "cell_type": "code", 461 | "execution_count": 39, 462 | "metadata": { 463 | "collapsed": false 464 | }, 465 | "outputs": [ 466 | { 467 | "data": { 468 | "text/plain": [ 469 | "[58, 38, 5, 82, 87, 86, 3, 6, 25, 2]" 470 | ] 471 | }, 472 | "execution_count": 39, 473 | "metadata": {}, 474 | "output_type": "execute_result" 475 | } 476 | ], 477 | "source": [ 478 | "_list" 479 | ] 480 | }, 481 | { 482 | "cell_type": "code", 483 | "execution_count": 40, 484 | "metadata": { 485 | "collapsed": false 486 | }, 487 | "outputs": [ 488 | { 489 | "data": { 490 | "text/plain": [ 491 | "[2, 3, 5, 6, 25, 38, 58, 82, 86, 87]" 492 | ] 493 | }, 494 | "execution_count": 40, 495 | "metadata": {}, 496 | "output_type": "execute_result" 497 | } 498 | ], 499 | "source": [ 500 | "insertionSort(_list)" 501 | ] 502 | }, 503 | { 504 | "cell_type": "markdown", 505 | "metadata": {}, 506 | "source": [ 507 | "## Working ith Sorted Lists\n", 508 | "\n", 509 | "We can increase the efficiency of some algorithms by making input list a sorted list. \n", 510 | "\n", 511 | "### Maintaining a Sorted List\n", 512 | "\n", 513 | "To maintain a sorted list new items must be inserted into their proper position. Instead of using ```append()``` method we have to locate proper position and use ```insert()``` method. \n" 514 | ] 515 | }, 516 | { 517 | "cell_type": "code", 518 | "execution_count": 42, 519 | "metadata": { 520 | "collapsed": true 521 | }, 522 | "outputs": [], 523 | "source": [ 524 | "# Modified version of the binary search that returns the index within \n", 525 | "# a sorted sequence indicating where the target should be located.\n", 526 | "def findSortedPosition(theList, target):\n", 527 | " low = 0 \n", 528 | " high = len(theList) - 1\n", 529 | " while low <= high:\n", 530 | " mid = (high + low) // 2\n", 531 | " if theList[mid] == target:\n", 532 | " # Index of the target\n", 533 | " return mid\n", 534 | " elif target < theList[mid]:\n", 535 | " high = mid - 1\n", 536 | " else:\n", 537 | " low = mid + 1\n", 538 | " \n", 539 | " # Index where the target value should be.\n", 540 | " return low" 541 | ] 542 | }, 543 | { 544 | "cell_type": "code", 545 | "execution_count": 47, 546 | "metadata": { 547 | "collapsed": true 548 | }, 549 | "outputs": [], 550 | "source": [ 551 | "_list = range(1,24,2)" 552 | ] 553 | }, 554 | { 555 | "cell_type": "code", 556 | "execution_count": 48, 557 | "metadata": { 558 | "collapsed": false 559 | }, 560 | "outputs": [ 561 | { 562 | "name": "stdout", 563 | "output_type": "stream", 564 | "text": [ 565 | "[1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23]\n" 566 | ] 567 | } 568 | ], 569 | "source": [ 570 | "print(_list)" 571 | ] 572 | }, 573 | { 574 | "cell_type": "code", 575 | "execution_count": 50, 576 | "metadata": { 577 | "collapsed": false 578 | }, 579 | "outputs": [ 580 | { 581 | "name": "stdout", 582 | "output_type": "stream", 583 | "text": [ 584 | "('Index is ', 6)\n" 585 | ] 586 | } 587 | ], 588 | "source": [ 589 | "print(\"Index is \", findSortedPosition(_list, 12))" 590 | ] 591 | } 592 | ], 593 | "metadata": { 594 | "anaconda-cloud": {}, 595 | "kernelspec": { 596 | "display_name": "Python [default]", 597 | "language": "python", 598 | "name": "python2" 599 | }, 600 | "language_info": { 601 | "codemirror_mode": { 602 | "name": "ipython", 603 | "version": 2 604 | }, 605 | "file_extension": ".py", 606 | "mimetype": "text/x-python", 607 | "name": "python", 608 | "nbconvert_exporter": "python", 609 | "pygments_lexer": "ipython2", 610 | "version": "2.7.12" 611 | } 612 | }, 613 | "nbformat": 4, 614 | "nbformat_minor": 1 615 | } 616 | -------------------------------------------------------------------------------- /Chapter5/README.md: -------------------------------------------------------------------------------- 1 | ## Chapter 5: Searching and Sorting 2 | 3 | ### To Do List: 4 | 5 | - [x] Finish chapter notes in ipynb 6 | - [x] Implement linear search algorithms with cases 7 | - [x] Implement binary search algorithm 8 | - [x] Implement Bubble sort algorithm 9 | - [x] Implement selection sort algorithm 10 | - [x] Implement insertion sort algorithm 11 | - [x] Working with Sorted Lists 12 | - [x] Maintaining a Sorted List 13 | - [x] Merging Sorted Lists 14 | - [x] Define and Implement Set ADT using sorted list 15 | - [ ] Modify and make necessary addition to Set ADT 16 | - [ ] Write a small snippet to verify usage of Set ADT 17 | - [ ] Define and Implement Bag ADT from chapter 1 again with sorted list and the binary search algorithm 18 | - [ ] Write a small snippet to verify usage of Bag ADT 19 | - [ ] Define and Implement Map ADT from chapter 3 again with sorted list and the binary search algorithm 20 | - [ ] Write a small snippet to verify usage of Map ADT 21 | - [ ] Define and Implement SparseLifeGrid ADT with sorted list and the binary search algorithm 22 | - [ ] Write a small snippet to verify usage of SparseLifeGrid ADT 23 | - [ ] Define and Implement ColorMap ADT 24 | - [ ] Write a small snippet to verify usage of ColorMap ADT 25 | -------------------------------------------------------------------------------- /Chapter5/binarysearch.py: -------------------------------------------------------------------------------- 1 | # Implementation of Binary Search algorithm 2 | 3 | import random 4 | 5 | def main(): 6 | _list = random.sample(xrange(1, 101), 10) 7 | value = 87 8 | print("Searching for the value: " + str(value)) 9 | if binarySearch(value): 10 | print("The number " + str(value) + " found in the list") 11 | else: 12 | print("The number " + str(value) + " not found in the list") 13 | 14 | def binarySearch(theValues, target): 15 | # Start with the entire sequence of elements. 0:length 16 | low = 0 17 | high = len(theValues - 1) 18 | 19 | # Repeatedly subdivide the sequence in half until the target is found. 20 | while low <= high: 21 | # Find the midpoint of the sequence. 22 | mid = (high + low) // 2 23 | # Does the midpoint contain the target? 24 | if theValues[mid] == target: 25 | return True 26 | # Or does the target precede the midpoint? 27 | elif target < theValues[mid]: 28 | high = mid - 1 # Update the upper bound 29 | 30 | # Or does it follow the midpoint 31 | else: 32 | low = mid + 1 # Update the lower bound 33 | 34 | # If the sequence cannot be subdivided further, we're done. 35 | return False 36 | 37 | 38 | if __name__ == '__main__': 39 | main() 40 | -------------------------------------------------------------------------------- /Chapter5/binaryset.py: -------------------------------------------------------------------------------- 1 | # Implementation of the Set ADT using a sorted list. 2 | 3 | class Set: 4 | # Creates an empty set instance. 5 | def __init__(self): 6 | self._theElements = list() 7 | 8 | # Returns the number of items in the set. 9 | def __len__(self): 10 | return len(self._theElements) 11 | 12 | # Determines if an element is in the set. 13 | def __contains__(self, element): 14 | ndx = self._findPosition(element) 15 | return ndx < len(self) and self._theElements[ndx] == element 16 | 17 | # Adds a new unique element to the set 18 | def add(self, element): 19 | if element not in self: 20 | ndx = self._findPosition(element) 21 | self._theElements.insert(ndx, element) 22 | 23 | # Removes an element from the set. 24 | def remove(self, element): 25 | assert element in self, "The element must be in the set." 26 | ndx = self._findPosition(element) 27 | self._theElements.pop(ndx) 28 | 29 | # Determines if this set is a subset of setB 30 | def isSubsetOf(self, setB): 31 | for element in self: 32 | if element not in setB: 33 | return False 34 | return True 35 | 36 | def __eq__(self, setB): 37 | if len(self) != len(setB): 38 | return False 39 | else: 40 | for i in range(len(self)): 41 | if self._theElements[i] != setB._theElements[i]: 42 | return False 43 | return True 44 | 45 | def union(self, setB): 46 | newSet = Set() 47 | a = 0 48 | b = 0 49 | # Merge the two lists together until one is empty. 50 | while a < len(self) and b < len(setB): 51 | valueA = self._theElements[a] 52 | valueb = setB._theElements[b] 53 | 54 | if valueA < valueB: 55 | newSet._theElements.append(valueA) 56 | a += 1 57 | elif valueA > valueB: 58 | newSet._theElements.append(valueB) 59 | b += 1 60 | # Only one of the two duplicates are appended 61 | else: 62 | newSet._theElements.append(valueA) 63 | a += 1 64 | b += 1 65 | 66 | # If a listA contains more items, append them to newList. 67 | while a < len(self): 68 | newSet._theElements.append(self._theElements[a]) 69 | a += 1 70 | 71 | # Or if listB contains more items, append them to newList. 72 | while b < len(setB): 73 | newSet._theElements.append(setB._theElements[b]) 74 | b += 1 75 | 76 | return newSet 77 | 78 | def difference(self, setB): 79 | newSet = Set() 80 | a = 0 81 | b = 0 82 | 83 | 84 | def intersect(self, setB): 85 | newSet = Set() 86 | a = 0 87 | b = 0 88 | 89 | # def isSubsetOf(self, setB): 90 | # newSet 91 | 92 | def __add__(self, setB): 93 | return self.union() 94 | 95 | def __sub__(self, setB): 96 | return self.difference() 97 | 98 | def __mul__(self): 99 | return self.intersect() 100 | 101 | 102 | def __iter__(self): 103 | return _SetIterator(self._theElements) 104 | 105 | def _findPosition(self, element): 106 | low = 0 107 | high = len(theList) - 1 108 | while low <= high: 109 | mid = (high - low) / 2 110 | if theList[mid] == target: 111 | return mid 112 | elif target < theList[mid]: 113 | high = mid - 1 114 | else: 115 | low = mid + 1 116 | 117 | return low 118 | 119 | # Iterator class for set __iter__ 120 | class _SetIterator: 121 | def __init__( self, theList ): 122 | self._setItems = theList 123 | self._curNdx = 0 124 | 125 | def __iter__( self ): 126 | return self 127 | 128 | def next( self ): 129 | if self._curNdx < len( self._setItems ): 130 | item = self._setItems[ self._curNdx ] 131 | self._curNdx += 1 132 | return item 133 | else: 134 | raise StopIteration 135 | -------------------------------------------------------------------------------- /Chapter5/bubblesort.py: -------------------------------------------------------------------------------- 1 | # Implementation of bubble sort algorithm 2 | 3 | import random 4 | 5 | def generateTest(length): 6 | return random.sample(xrange(1, 101), length) 7 | 8 | 9 | def main(): 10 | given_list = [80,7,24,16,43,91,35,2,19,72] 11 | print("Given Randomized list: ", given_list) 12 | print("Sorted List: ", bubbleSort(given_list)) 13 | 14 | for _ in range(1): 15 | _list = generateTest(5) 16 | print("Randomized list: ", _list) 17 | print("Sorted List: ", bubbleSort(_list)) 18 | 19 | # Sorts a sequence in ascending order using the bubble sort algorith. 20 | def bubbleSort(seq): 21 | not_sorted = True 22 | n = len(seq) 23 | # print "At the beginning: " 24 | # print seq 25 | while not_sorted: 26 | # If following statements fail next statement will stop the loop 27 | not_sorted = False 28 | for i in range(n-1): 29 | if seq[i] <= seq[i+1]: 30 | continue; 31 | else: 32 | temp = seq[i] 33 | seq[i] = seq[i+1] 34 | seq[i+1] = temp 35 | 36 | not_sorted = True 37 | # print seq 38 | return seq 39 | 40 | 41 | if __name__ == '__main__': 42 | main() 43 | -------------------------------------------------------------------------------- /Chapter5/findsortedposition.py: -------------------------------------------------------------------------------- 1 | 2 | import random 3 | 4 | def main(): 5 | _list = range(1,24,2) 6 | print(_list) 7 | print("The index we need to put: ") 8 | print(findSortedPosition(12)) 9 | 10 | 11 | 12 | # Modified version of the binary search that returns the index within 13 | # a sorted sequence indicating where the target should be located. 14 | def findSortedPosition(theList, target): 15 | low = 0 16 | high = len(theList) - 1 17 | while low <= high: 18 | mid = (high + low) // 2 19 | if theList[mid] == target: 20 | # Index of the target 21 | return mid 22 | elif target < theList[mid]: 23 | high = mid - 1 24 | else: 25 | low = mid + 1 26 | 27 | # Index where the target value should be. 28 | return low 29 | 30 | if __name__ == '__main__': 31 | main() 32 | -------------------------------------------------------------------------------- /Chapter5/insertionSort.py: -------------------------------------------------------------------------------- 1 | # Implementation of insertion sort algorithm 2 | 3 | import random 4 | 5 | def generateTest(length): 6 | return random.sample(xrange(1, 101), length) 7 | 8 | 9 | def main(): 10 | given_list = [80,7,24,16,43,91,35,2,19,72] 11 | print("Given Randomized list: ", given_list) 12 | print("Sorted List: ", insertionSort(given_list)) 13 | 14 | for _ in range(1): 15 | _list = generateTest(5) 16 | print("Randomized list: ", _list) 17 | print("Sorted List: ", insertionSort(_list)) 18 | 19 | # Sorts a sequence in ascending order using the insertion sort algorithm. 20 | def insertionSort(theSeq): 21 | n = len(theSeq) 22 | # Starts with the first item as the only sorted entry. 23 | for i in range(1, n): 24 | # Save the value to be positioned. 25 | value = theSeq[i] 26 | # Find the position where value fits in the ordered part of the list. 27 | pos = i 28 | while pos > 0 and value < theSeq[pos - 1]: 29 | # Shift the items to the rigth during search 30 | theSeq[pos] = theSeq[pos - 1] 31 | pos -= 1 32 | 33 | theSeq[pos] = value 34 | return theSeq 35 | 36 | 37 | if __name__ == '__main__': 38 | main() 39 | -------------------------------------------------------------------------------- /Chapter5/linearsearch.py: -------------------------------------------------------------------------------- 1 | # Implementation of linearsearch functions. 2 | 3 | import random 4 | 5 | def main(): 6 | 7 | _list = random.sample(xrange(1, 101), 10) 8 | print("Finding Smallest number, " + str(findSmallest(_list))) 9 | print("Finding Biggest number, " + str(findBiggest(_list))) 10 | 11 | def linearSearch(theValues, target): 12 | n = len(theValues) 13 | for i in range(n): 14 | # If the target is in the ith element, return True 15 | if theValues[i] == target: 16 | return True 17 | # If not found, return False. 18 | return False 19 | 20 | def sortedLinearSearch(theValues, item): 21 | n = len(theValues) 22 | for i in range(n): 23 | # If the target is found in the ith element, return True 24 | if theValues[i] == item: 25 | return True 26 | # If target is largers than the ith element, it's not in the sequence. 27 | elif theValues[i] > item: 28 | return False 29 | 30 | # The item is not in the sequence. 31 | return False 32 | 33 | def findSmallest(theValues): 34 | n = len(theValues) 35 | # Assume the first item is the smallest value 36 | smallest = theValues[0] 37 | # Determine if any other item in the sequence is smaller. 38 | for i in range(1,n): 39 | if theValues[i] < smallest: 40 | smallest = theValues[i] 41 | # Return the smallest found. 42 | return smallest 43 | 44 | def findBiggest(theValues): 45 | n = len(theValues) 46 | # Assuming the first item is the biggest value 47 | biggest = theValues[0] 48 | # Determine if any other item in the sequence is bigger. 49 | for i in range(1, n): 50 | if theValues[i] > biggest: 51 | biggest = theValues[i] 52 | #Return the biggest found. 53 | return biggest 54 | 55 | 56 | if __name__ == '__main__': 57 | main() 58 | -------------------------------------------------------------------------------- /Chapter5/mergeSortedList.py: -------------------------------------------------------------------------------- 1 | # Implementation of 2 sorted list merging 2 | # The lists should be sorted before hand 3 | 4 | import random 5 | 6 | def main(): 7 | _listA = range(1,40,5) 8 | _listB = range(1,40,8) 9 | print("List 1: ", _listA) 10 | print("List 2: ", _listB) 11 | print("Merged and Sorted List: ") 12 | print(mergeSortedLists(_listA, _listB)) 13 | 14 | # Merges two sorted list to create and return a new sorted list. 15 | def mergeSortedLists(listA, listB): 16 | # Create a new list and initialize the list markers 17 | newList = list() 18 | a = 0 19 | b = 0 20 | 21 | # Merge the two lists together until one is empty. 22 | while a < len(listA) and b < len(listB): 23 | if listA[a] < listB[b]: 24 | newList.append(listA[a]) 25 | a += 1 26 | else: 27 | newList.append(listB[b]) 28 | b += 1 29 | 30 | # If listA contains more items, append them to newList 31 | while a < len(listA): 32 | newList.append(listA[a]) 33 | a += 1 34 | 35 | # Or if listB contains more items, append them to newList 36 | while b < len(listB): 37 | newList.append(listB[b]) 38 | b += 1 39 | 40 | return newList 41 | 42 | 43 | if __name__ == '__main__': 44 | main() 45 | -------------------------------------------------------------------------------- /Chapter5/selectionsort.py: -------------------------------------------------------------------------------- 1 | # Implementation of selection sort algorithm 2 | 3 | import random 4 | 5 | def generateTest(length): 6 | return random.sample(xrange(1, 101), length) 7 | 8 | 9 | def main(): 10 | given_list = [80,7,24,16,43,91,35,2,19,72] 11 | print("Given Randomized list: ", given_list) 12 | print("Sorted List: ", selectionSort(given_list)) 13 | 14 | for _ in range(1): 15 | _list = generateTest(5) 16 | print("Randomized list: ", _list) 17 | print("Sorted List: ", selectionSort(_list)) 18 | 19 | # Sorts a sequence in ascending order using the selection sort algorithm 20 | def selectionSort(theSeq): 21 | n = len(theSeq) 22 | for i in range(n-1): 23 | # Assume the ith element is the smallest. 24 | smallNdx = i 25 | for j in range(i+1, n): 26 | if theSeq[j] < theSeq[smallNdx]: 27 | smallNdx = j 28 | 29 | # Swap the ith value and smallNdx value only if the smallest value is 30 | # not really in its proper position. Some implementations omit testing 31 | # the condition and always swap the two values. 32 | if smallNdx != i: 33 | tmp = theSeq[i] 34 | theSeq[i] = theSeq[smallNdx] 35 | theSeq[smallNdx] = tmp 36 | 37 | return theSeq 38 | 39 | 40 | if __name__ == '__main__': 41 | main() 42 | -------------------------------------------------------------------------------- /Chapter6/6-Linked_Structures.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Linked Structures\n", 8 | "\n", 9 | "- **Arrays** are basic sequence containe with easy and direct access to the individual elements however they are limited in their functionality\n", 10 | "- **Python lists** implemented using an array structure, which extends arrays' functionality by providing largers sets of operations. \n", 11 | "\n", 12 | "- Fixed array size, insertion and deletion times, are some problems of Arrays, and Python lists. \n", 13 | "- **Linked list** data structure can be used to store a collection in linear order. \n", 14 | "- Several variaties of linked liss are singly linked lists, and doubly linked lists.\n", 15 | "\n", 16 | "## Introduction\n", 17 | "\n", 18 | "Let's create a basic class containing sigle data field:" 19 | ] 20 | }, 21 | { 22 | "cell_type": "code", 23 | "execution_count": 1, 24 | "metadata": { 25 | "collapsed": true 26 | }, 27 | "outputs": [], 28 | "source": [ 29 | "class ListNode:\n", 30 | " def __init__(self, data):\n", 31 | " self.data = data" 32 | ] 33 | }, 34 | { 35 | "cell_type": "markdown", 36 | "metadata": {}, 37 | "source": [ 38 | "This is will give us just the containers that we can store data into." 39 | ] 40 | }, 41 | { 42 | "cell_type": "code", 43 | "execution_count": 2, 44 | "metadata": { 45 | "collapsed": true 46 | }, 47 | "outputs": [], 48 | "source": [ 49 | "a = ListNode(11)\n", 50 | "b = ListNode(52)\n", 51 | "c = ListNode(18)" 52 | ] 53 | }, 54 | { 55 | "cell_type": "code", 56 | "execution_count": 4, 57 | "metadata": {}, 58 | "outputs": [ 59 | { 60 | "name": "stdout", 61 | "output_type": "stream", 62 | "text": [ 63 | "<__main__.ListNode object at 0x7f57e8588b38>\n" 64 | ] 65 | } 66 | ], 67 | "source": [ 68 | "print(a)" 69 | ] 70 | }, 71 | { 72 | "cell_type": "markdown", 73 | "metadata": {}, 74 | "source": [ 75 | "Since we did not define a method to show the value stored, we cannot see the value. However the values we passed to each variable is stored. \n", 76 | "\n", 77 | "---\n", 78 | "\n", 79 | "Now to make it a **linked** list, we have to establish a connection. To achieve this we can add another data field called *next* into our constructor." 80 | ] 81 | }, 82 | { 83 | "cell_type": "code", 84 | "execution_count": 5, 85 | "metadata": { 86 | "collapsed": true 87 | }, 88 | "outputs": [], 89 | "source": [ 90 | "class ListNode:\n", 91 | " def __init__(self, data):\n", 92 | " self.data = data\n", 93 | " self.next = None" 94 | ] 95 | }, 96 | { 97 | "cell_type": "code", 98 | "execution_count": 6, 99 | "metadata": { 100 | "collapsed": true 101 | }, 102 | "outputs": [], 103 | "source": [ 104 | "# Initial creation of nodes\n", 105 | "a = ListNode(11)\n", 106 | "b = ListNode(52)\n", 107 | "c = ListNode(18)\n" 108 | ] 109 | }, 110 | { 111 | "cell_type": "markdown", 112 | "metadata": {}, 113 | "source": [ 114 | "After modifying the ListNode class and creating nodes for testing we need to use next field to assign it to next node to establish a connection." 115 | ] 116 | }, 117 | { 118 | "cell_type": "code", 119 | "execution_count": 7, 120 | "metadata": { 121 | "collapsed": true 122 | }, 123 | "outputs": [], 124 | "source": [ 125 | "a.next = b\n", 126 | "b.next = c" 127 | ] 128 | }, 129 | { 130 | "cell_type": "code", 131 | "execution_count": 11, 132 | "metadata": {}, 133 | "outputs": [ 134 | { 135 | "name": "stdout", 136 | "output_type": "stream", 137 | "text": [ 138 | "11\n", 139 | "52\n", 140 | "18\n" 141 | ] 142 | } 143 | ], 144 | "source": [ 145 | "print(a.data) # Prints the first node\n", 146 | "print(a.next.data) # Prints the b\n", 147 | "print(a.next.next.data) # Prints the c" 148 | ] 149 | }, 150 | { 151 | "cell_type": "markdown", 152 | "metadata": {}, 153 | "source": [ 154 | "- Linked Structure contains **nodes** with data inside them and at least one reference or **link** to another. \n", 155 | "- Last node is commonly called the **tail** node, which is indicated by a null link reference.\n", 156 | "- The first node in the list is referenced by an external variable as it provides an entry point into the linked list, this variable called **head reference**. \n", 157 | "\n", 158 | "![Singly Linked List in nutshell](http://www.java2novice.com/images/linked_list.png)\n", 159 | "\n", 160 | "---" 161 | ] 162 | }, 163 | { 164 | "cell_type": "markdown", 165 | "metadata": {}, 166 | "source": [ 167 | "## The Singly Linked List\n", 168 | "\n", 169 | "- Linked Lists with single connection allowed. \n", 170 | "\n", 171 | "### 1. Traversing the Nodes\n", 172 | "\n", 173 | "Connecting all the nodes together in large list of nodes is impractical with as we did in the early example. Instead we will use a temporary external reference to traverse through the list, moving the reference along as we access the individual nodes. \n", 174 | "\n", 175 | "```Python\n", 176 | "def traversal(head):\n", 177 | " curNode = head\n", 178 | " while curNode is not None:\n", 179 | " print(curNode.data)\n", 180 | " curNode = curNode.next\n", 181 | "```\n", 182 | "*A complete list traversal requires O(n) time.*\n", 183 | "\n", 184 | "### 2. Searching for a Node\n", 185 | "Linear search operation can be performed on a linked list, using the same principle we did with traversal.\n", 186 | "\n", 187 | "```Python\n", 188 | "def unorderedSearch(head, target):\n", 189 | " curNode = head\n", 190 | " while curNode is not None and curNode.data != target:\n", 191 | " curNode = curNode.next\n", 192 | " return curNode is not None\n", 193 | "```\n", 194 | "*Search operation requires O(n) in the worst case*\n", 195 | "\n", 196 | "### 3. Prepending Nodes\n", 197 | "Prepending a node can be done in constant time since no traversal is required, because we simply maintain the head reference. One simple example of prepending:\n", 198 | "\n", 199 | "```Python\n", 200 | "newNode = ListNode(item)\n", 201 | "newNode.next = head\n", 202 | "head = newNode\n", 203 | "```\n", 204 | "\n", 205 | "When we add a new value here are the steps followed:\n", 206 | "\n", 207 | "- Create a new node to store the new value\n", 208 | "- Set it's next field to point to the node currently at the front of the list\n", 209 | "- Adjust head to point to the new node since it's now the first node\n", 210 | "\n", 211 | "![Prepending a node to the linked list](./figures/addnode2linkedlist.png)\n", 212 | "\n", 213 | "> Careful of the order of the new links, because if we link the new node into the list before modifying the head reference we lose our external reference, which results losing the list itself.\n", 214 | "\n", 215 | "### 4. Removing Nodes\n", 216 | "Removing nodes or unlinking them will simply extract them from the linked list structure. The steps:\n", 217 | "\n", 218 | "- Find the node containing value and position the external reference pointing to it. \n", 219 | "- Unlink from the list by setting node's link field to ```None```\n", 220 | "- Access the node's successor by assigning extra next link to node. \n", 221 | "\n", 222 | "![Deleting a node from a linked list:](./figures/removeANode.png)\n", 223 | "\n", 224 | "> Removing the first node is a special case, because head references the node.\n", 225 | "\n", 226 | "```Python\n", 227 | "# Given the head reference, remove a target from a linked list.\n", 228 | "predNode = None\n", 229 | "curNode = head\n", 230 | "\n", 231 | "while curNode is not None and curNode.data != target:\n", 232 | " predNode = curNode\n", 233 | " curNode = curNode.next\n", 234 | "\n", 235 | "if curNode is not None:\n", 236 | " if curNode is head:\n", 237 | " head = curNode.next\n", 238 | " else:\n", 239 | " predNode.next = curNode.next\n", 240 | "```\n", 241 | "\n", 242 | "---" 243 | ] 244 | }, 245 | { 246 | "cell_type": "markdown", 247 | "metadata": {}, 248 | "source": [ 249 | "## More ways to Build a Linked List\n", 250 | "\n", 251 | "Sometimes we need extra flexibility in our linked list than having only basic container with linear order, that's why now we'll see some features.\n", 252 | "\n", 253 | "### Using a Tail Reference\n", 254 | "\n", 255 | "Using a both head and tail might save us some time, when we need to append nodes to end of the linked list. \n", 256 | "\n", 257 | "__Adding Node using Tail Reference:__\n", 258 | "\n", 259 | "```Python\n", 260 | "# Given the head and tail pointers, adds an item to a linked list.\n", 261 | "newNode = ListNode(item)\n", 262 | "if head is None:\n", 263 | " head = newNode\n", 264 | "else:\n", 265 | " tail.next = newNode\n", 266 | "\n", 267 | "tail = newNode\n", 268 | "```\n", 269 | "\n", 270 | "__Removing Node using Tail Reference:__\n", 271 | "\n", 272 | "```Python\n", 273 | "# Given the head and tail references, removes a target from a linked list. \n", 274 | "predNode = None\n", 275 | "curNode = head\n", 276 | "\n", 277 | "while curNode is not None and curNode.data != target:\n", 278 | " predNode = curNode\n", 279 | " curNode = curNode.next\n", 280 | " \n", 281 | "if curNode is not None:\n", 282 | " if curNode is head:\n", 283 | " head = curNode.next\n", 284 | " else:\n", 285 | " predNode.next = curNode.next\n", 286 | " \n", 287 | " if curNode is tail:\n", 288 | " tail = predNode\n", 289 | "```\n", 290 | "\n", 291 | "\n", 292 | "### The Sorted Linked List\n", 293 | "\n", 294 | "We can store elements in linked list as sorted.\n", 295 | "\n", 296 | "__Linear Search__\n", 297 | "\n", 298 | "Linear search in sorted linked list will be faster since we add extra condition to terminates the loop when we reach the bigger number. \n", 299 | "\n", 300 | "```Python\n", 301 | "def sortedSearch(head, target):\n", 302 | " curNode = head\n", 303 | " while curNode is not None and curNode.data < target:\n", 304 | " if curNode.data == target:\n", 305 | " return True\n", 306 | " else:\n", 307 | " curNode = node.next\n", 308 | " return False\n", 309 | "```\n", 310 | "\n", 311 | "__Inserting Nodes__\n", 312 | "\n", 313 | "Adding a node to sorted linked list a little bit more trickier, since we have to find the spot to put our Node into sorted list.\n", 314 | "\n", 315 | "\n", 316 | "```Python\n", 317 | "# Given the head pointer, insert a value into a sorted linked list.\n", 318 | "@ToDo: # Review and fix the function \n", 319 | "def insert2sorted(head):\n", 320 | " # Find the insertion point for the new value.\n", 321 | " predNode = None\n", 322 | " curNode = head\n", 323 | "\n", 324 | " while curNode is not None and value > curNode.data:\n", 325 | " predNode = curNode\n", 326 | " curNode = curNode.next\n", 327 | "\n", 328 | " # Create the new node for the new value.\n", 329 | " newNode = ListNode(value)\n", 330 | " newNode.next = curNode\n", 331 | " # Link the new node into the list.\n", 332 | " if curNode is head:\n", 333 | " head = newNode\n", 334 | " else:\n", 335 | " predNode.next = newNode\n", 336 | "```\n", 337 | "\n", 338 | "__Traversing and Deleting__\n", 339 | "\n", 340 | "It's pretty much the same for sorted linked lists as it was in normal linked lists.\n", 341 | "\n", 342 | "---" 343 | ] 344 | }, 345 | { 346 | "cell_type": "code", 347 | "execution_count": null, 348 | "metadata": { 349 | "collapsed": true 350 | }, 351 | "outputs": [], 352 | "source": [] 353 | } 354 | ], 355 | "metadata": { 356 | "kernelspec": { 357 | "display_name": "Python 3", 358 | "language": "python", 359 | "name": "python3" 360 | }, 361 | "language_info": { 362 | "codemirror_mode": { 363 | "name": "ipython", 364 | "version": 3 365 | }, 366 | "file_extension": ".py", 367 | "mimetype": "text/x-python", 368 | "name": "python", 369 | "nbconvert_exporter": "python", 370 | "pygments_lexer": "ipython3", 371 | "version": "3.6.1" 372 | } 373 | }, 374 | "nbformat": 4, 375 | "nbformat_minor": 2 376 | } 377 | -------------------------------------------------------------------------------- /Chapter6/README.md: -------------------------------------------------------------------------------- 1 | ## Chapter 6: Linked Structures 2 | 3 | ### To Do List: 4 | 5 | - [X] Finish chapter notes in ipynb 6 | - [X] Define and Implement Node Class to use in following Implementations 7 | - [X] Define and Implement Single Linked List Implementation 8 | - [X] Add examples and verify that Linked List Structure is Working 9 | - [X] Define and Implement Bag ADT using Linked Lists 10 | - [X] Write a small snippet to verify usage of Bag ADT 11 | - [X] Define and Implement SparseMatrix ADT using Linked Lists 12 | - [X] Write a small snippet to verify usage of SparseMatrix ADT 13 | - [X] Define and Implement Polynomials ADT using Linked Lists 14 | - [X] Write a small snippet to verify usage of Polynomials ADT 15 | - [ ] Modify and make necessary addition to Single Linked List Implementation 16 | - [ ] Modify and make necessary addition to SparseMatrix ADT built this chapter 17 | - [ ] Complete Bag ADT by adding essential methods 18 | - [ ] Implement a new version of Set ADT using an unsorted linked list 19 | - [ ] Implement a new version of Set ADT using a sorted linked list 20 | - [ ] Implement a new version of vector ADT using an unsorted linked list 21 | - [ ] Implement a new version of Map ADT using an unsorted linked list 22 | - [ ] Implement a new version of Map ADT using a sorted linked list 23 | - [ ] Modify and make necessary addition to Polynomials ADT 24 | - [ ] Define and Implement BigInteger ADT 25 | -------------------------------------------------------------------------------- /Chapter6/figures/addnode2linkedlist.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eneskemalergin/Data_Structures_and_Algorithms/3bc8db479e75fb6cb289c899727f7f44870af823/Chapter6/figures/addnode2linkedlist.png -------------------------------------------------------------------------------- /Chapter6/figures/removeANode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eneskemalergin/Data_Structures_and_Algorithms/3bc8db479e75fb6cb289c899727f7f44870af823/Chapter6/figures/removeANode.png -------------------------------------------------------------------------------- /Chapter6/llistbag.py: -------------------------------------------------------------------------------- 1 | # Implementation of Bag ADT using Singly Linked List 2 | 3 | class Bag: 4 | # Construct an empty bag 5 | def __init__(self): 6 | self._head = None 7 | self._size = 0 8 | 9 | # Return the number of items in the bag. 10 | def __len__(self): 11 | return self._size 12 | 13 | # Determine if an item is contained in the bag. 14 | def __contains__(self, target): 15 | curNode = self._head 16 | while curNode is not None and curNode.item != target: 17 | curNode = curNode.next 18 | return curNode is not None 19 | 20 | # Add a new item to the bag 21 | def add(self, item): 22 | # TODO: Review here if I could use the Node class 23 | newNode = _BagListNode(item) # Takes advantage of private storage class 24 | newNode.next = self._head 25 | self._head = newNode 26 | self._size += 1 27 | 28 | # Remove an instance of the item from the bag 29 | def remove(self, item): 30 | predNode = None 31 | curNode = self._head 32 | while curNode is not None and curNode.item != item: 33 | predNode = curNode 34 | curNode = curNode.next 35 | 36 | # Check if item is even in the list otherwise return error 37 | assert curNode is not None, "The item must be in the bag." 38 | 39 | # Unlink the node and return the item. 40 | self._size -= 1 41 | if curNode is self._head: 42 | self._head = curNode.next 43 | else: 44 | predNode.next = curNode.next 45 | return curNode.item 46 | 47 | # Return the iterator for traversing the list of items. 48 | def __iter__(self): 49 | return _BagIterator(self._head) 50 | 51 | # Define a private storage class for creating list nodes. 52 | class _BagListNode(object): 53 | def __init__(self, item): 54 | self.item = item 55 | self.next = None 56 | 57 | # Define a linked list iterator for the Bag ADT 58 | class _BagIterator: 59 | def __init__(self, listHead): 60 | self._curNode = listHead 61 | 62 | def __iter__(self): 63 | return self 64 | 65 | def __next__(self): 66 | if self._curNode is None: 67 | raise StopIteration 68 | else: 69 | item = self._curNode.item 70 | self._curNode = self._curNode.next 71 | return item 72 | -------------------------------------------------------------------------------- /Chapter6/llistbag_test.py: -------------------------------------------------------------------------------- 1 | # Testing script for linked list implementation of Bag ADT 2 | from llistbag import Bag 3 | 4 | def main(): 5 | print("Testing class constructor:") 6 | LLBag = Bag() 7 | 8 | print("Testing add method:") 9 | LLBag.add(45) 10 | LLBag.add(25) 11 | LLBag.add(52) 12 | LLBag.add(17) 13 | 14 | print("Testing in method:") 15 | print(45 in LLBag) 16 | 17 | print("Testing iteration method:") 18 | for i in LLBag: 19 | print(i) 20 | 21 | print("Testing remove method:") 22 | LLBag.remove(17) 23 | LLBag.remove(18) 24 | 25 | if __name__ == '__main__': 26 | main() 27 | -------------------------------------------------------------------------------- /Chapter6/llistsparse.py: -------------------------------------------------------------------------------- 1 | # Implementation of the Sparse Matrix ADT using and array linked lists. 2 | from array import Array 3 | 4 | class SparseMatrix: 5 | # Create a sparse matrix of size numRows x numCols initialized to 0. 6 | def __init__(self, numRows, numCols): 7 | self._numCols = numCols 8 | self.listOfRows = Array(numRows) 9 | 10 | # Return the number of rows in the matrix 11 | def numRows(self): 12 | return len(self._listOfRows) 13 | 14 | # Return the number of columns in the matrix 15 | def numCols(self): 16 | return self._numCols 17 | 18 | # Return the value of element (i,j): x[i,j] 19 | def __getitem__(self, ndxTuple): 20 | # TODO: Complete the method 21 | pass 22 | 23 | # Set the value of element (i,j) to the value s: x[i,j] = s 24 | def __setitem__(self, ndxTuple, value): 25 | predNode = None 26 | curNode = self._listOfRows[row] 27 | while curNode is not None and curNode.col != col: 28 | predNode = curNode 29 | curNode = curNode.next 30 | 31 | # See if the element is in the list 32 | if curNode is not None and curNode.col == col: 33 | # Remove the node 34 | if value == 0.0: 35 | if curNode == self._listOfRows[row]: 36 | self._listOfRows[row] = curNode.next 37 | else: 38 | predNode.next = curNode.next 39 | else: # Change the node's value 40 | curNode.value = value 41 | 42 | # Otherwise, the element is not in the list. 43 | elif value != 0.0: 44 | newNode = _MatrixElementNode(col, value) 45 | newNode.next == curNode 46 | if curNode == self._listOfRows[row]: 47 | self._listOfRows[row] = newNode 48 | else: 49 | predNode.next = newNode 50 | 51 | # Scale the matrix by the given scalar 52 | def scaleBy(self, scalar): 53 | for row in range(self.numRows()): 54 | curNode = self._listOfRows[row] 55 | while curNode is not None: 56 | curNode.value *= scalar 57 | curNode = curNode.next 58 | 59 | # Create and return to a transpose of this matrix 60 | def transpose(self): 61 | # TODO: Complete the method 62 | pass 63 | 64 | # Matrix addition: newMatrix = self + rhsMatrix 65 | def __add__(self, rhsMatrix): 66 | # Making sure the two matrices have the correct size. 67 | assert rhsMatrix.numRows() == self.numRows() and \ 68 | rhsMatrix.numCols() == self.numCols(), \ 69 | "Matrix sizes are not compatible for adding!" 70 | 71 | # Create a new sparse matrix of the same size. 72 | newMatrix = SparseMatrix(self.numRows(), self.numCols()) 73 | 74 | # Add the elements of this matrix to the new matrix. 75 | for row in range(self.numRows()): 76 | curNode = self._listOfRows[row] 77 | while curNode is not None: 78 | newMatrix[row, curNode.col] = curNode.value 79 | curNode = curNode.next 80 | 81 | # Add the elements of the rhsMatrix to the new matrix. 82 | for row in range(rhsMatrix.numRows()): 83 | curNode = rhsMatrix._listOfRows[row] 84 | while curNode is not None: 85 | value = newMatrix[row, curNode.col] 86 | value += curNode.value 87 | newMatrix[row, curNode.col] = value 88 | curNode = curNode.next 89 | 90 | return newMatrix 91 | 92 | # TODO: 1- Add Matrix Substraction: __sub__ 93 | # TODO: 2- Add Matrix Multiplication: __mul__ 94 | 95 | # Storage class for creating matrix element nodes. 96 | class _MatrixElementNode: 97 | def __init__(self, col, value): 98 | self.col = col 99 | self.value = value 100 | self.next = None 101 | -------------------------------------------------------------------------------- /Chapter6/llistsparse_test.py: -------------------------------------------------------------------------------- 1 | # Testing script for linked list implementation of Sparse Matrix ADT 2 | 3 | from llistsparse import SparseMatrix 4 | 5 | def main(): 6 | print("Testing class constructor:") 7 | LLSMatrix = SparseMatrix(3,3) # Creating a zero sparse matrix 3x3 8 | 9 | print("Testing numRows method:") 10 | print(LLSMatrix.numRows()) 11 | 12 | print("Testing numCols method:") 13 | print(LLSMatrix.numCols()) 14 | 15 | print("Testing getitem method:") 16 | # This method is not complete 17 | # print(LLSMatrix[1,2]) 18 | 19 | print("Testing setitem method:") 20 | # If error not occured everything works. 21 | LLSMatrix[1,2] = 1 # Setting item to the given index location 22 | 23 | print("Testing scaleBy method: ") 24 | LLSMatrix.scaleBy(2) 25 | 26 | print("Testing transpose method: ") 27 | # This method is not complete 28 | # LLSMatrix.transpose() 29 | 30 | print("Testing add method: ") 31 | # To test this second sparse matrix is needed 32 | LLSMatrix2 = SparseMatrix(4,5) 33 | LLSMatrix3 = SparseMatrix(3,2) 34 | temp = LLSMatrix + LLSMatrix3 # This should be able to work 35 | new_temp = LLSMatrix + LLSMatrix2 36 | 37 | print("Testing substraction method: ") 38 | # This method is not complete 39 | # temp = LLSMatrix - LLSMatrix3 # This should be able to work 40 | # new_temp = LLSMatrix - LLSMatrix2 # This should give error 41 | 42 | 43 | if __name__ == '__main__': 44 | main() 45 | -------------------------------------------------------------------------------- /Chapter6/node.py: -------------------------------------------------------------------------------- 1 | # Implementation of Node for the Linked List Classes 2 | 3 | class Node: 4 | def __init__(self, data): 5 | self.data = data 6 | self.next = None 7 | 8 | def __str__(self): 9 | return str(self.data) 10 | 11 | def get_data(self): 12 | return self.data 13 | 14 | def get_next(self): 15 | return self.next 16 | 17 | def set_next(self, new_next): 18 | self.next = new_next 19 | 20 | def iter(self): 21 | node = self 22 | while True: 23 | yield node.value 24 | if node.next: 25 | node = node.next 26 | else: 27 | raise StopIteration() 28 | -------------------------------------------------------------------------------- /Chapter6/polynomial.py: -------------------------------------------------------------------------------- 1 | # Implementation of the Polynomial ADT using and array linked lists. 2 | 3 | class Polynomial: 4 | # Create a new polynomial object. 5 | def __init__(self, degree=None, coefficient=None): 6 | if degree is None: 7 | self._polyHead = None 8 | else: 9 | self._polyHead = _PolyTermNode(degree, coefficient) 10 | 11 | self._polyTail = self._polyHead 12 | 13 | # Return the degree of the polynomial. 14 | def degree(self): 15 | if self._polyHead is None: 16 | return -1 17 | else: 18 | return self._polyHead.degree 19 | 20 | # Return the coefficient for the term of the given degree. 21 | def __getitem__(self, degree): 22 | assert self.degree() >= 0, "Operation not permitted on empty polynomial." 23 | curNode = self._polyHead 24 | while curNode is not None and curNode.degree >= degree: 25 | curNode = curNode.next 26 | 27 | if curNode is None or curNode.degree != degree: 28 | return 0.0 29 | else: 30 | return curNode.degree 31 | 32 | # Evaluate the polynomial at the given scalar value. 33 | def evaluate(self, scalar): 34 | assert self.degree() >=0, "Only non-empty polynomials can be evaluated." 35 | result = 0.0 36 | curNode = self._polyHead 37 | while curNode is not None: 38 | result += curNode.coefficient * (scalar ** curNode.degree) 39 | curNode = curNode.next 40 | return result 41 | 42 | # Simple Brute Force method for polynomial addition 43 | def simple_add(self, rhsPoly): 44 | # REVIEW: Following line is not make sense 45 | newPoly = Polynomial() 46 | if self.degree() > rhsPoly.degree(): 47 | maxDegree = self.degree() 48 | else: 49 | maxDegree = rhsPoly.degree() 50 | 51 | i = maxDegree 52 | while i >= 0: 53 | value = self[i] +rhsPoly[i] 54 | self.__appendTerm[i, value] 55 | i += 1 56 | # REVIEW: Ok, we are using it here but how we are changing it? 57 | return newPoly 58 | 59 | # Polynomial addition: newPoly = self + rhsPoly 60 | def __add__(self, rhsPoly): 61 | assert self.degree() >= 0 and rhsPoly.degree() >= 0, 62 | "Addition only allowed on non-empty polynomials." 63 | 64 | newPoly = Polynomial() 65 | nodeA = self._termList 66 | nodeB = rhsPoly._termList 67 | 68 | # Add corresponding terms until one list is empty. 69 | while nodeA is not None and nodeB is not None: 70 | if nodeA.degree > nodeB.degree: 71 | degree = nodeA.degree 72 | value = nodeA.coefficient 73 | nodeA = nodeA.next 74 | # elif listA.degree < listB.degree: Attention: This is a typo in the book 75 | elif nodeA.degree < nodeB.degree: 76 | degree = nodeB.degree 77 | value = nodeB.coefficient 78 | nodeB = nodeB.next 79 | else: 80 | degree = nodeA.degree 81 | value = nodeA.coefficient + nodeB.coefficient 82 | nodeA = nodeA.next 83 | nodeB = nodeB.next 84 | 85 | self._appendTerm(degree, value) 86 | 87 | # If self list contains more terms append them. 88 | while nodeA is not None: 89 | self._appendTerm(nodeA.degree, nodeA.coefficient) 90 | nodeA = nodeA.next 91 | 92 | # or if rhs contains more terms append them. 93 | while nodeB is not None: 94 | self._appendTerm(nodeB.degree, nodeB.coefficient) 95 | nodeB = nodeB.next 96 | 97 | return newPoly 98 | 99 | # Polynomial substraction: newPoly = self - rhsPoly 100 | def __sub__(self, rhsPoly): 101 | pass 102 | 103 | # Polynomial multiplication: newPoly = self * rhsPoly 104 | def __mul__(self, rhsPoly): 105 | pass 106 | 107 | def multiply(self, rhsPoly): 108 | assert self.degree() >= and rhsPoly.degree() >= 0, 109 | "Multiplication only allowed on non-empty polynomials." 110 | 111 | # Create a new polynomial by multiplying rhsPoly by the first term. 112 | node = self._polyHead 113 | newPoly = rhsPoly._termMultiply(node) 114 | 115 | # Iterate through the remaining terms of the poly computing 116 | # product of the rhsPoly by each term. 117 | node = node.next 118 | while node is not None: 119 | tempPoly = rhsPoly._termMultiply(node) 120 | newPoly = newPoly.add(tempPoly) 121 | node = node.next 122 | 123 | return newPoly 124 | 125 | # Helper method for creating a new polynomial from multiplying an 126 | # existing polynomial by another term. 127 | def _termMultiply(self, termNode): 128 | newPoly = Polynomial() 129 | 130 | # Iterate through the terms and compute the product of each term and 131 | # the term in termNode 132 | 133 | curr = curr.next 134 | while curr is not None: 135 | # Compute the product of the term. 136 | newDegree = curr.degree + termNode.degree 137 | newCoeff = curr.coefficient * termNode.coefficient 138 | 139 | # Append it to the new polynomial. 140 | newPoly._appendTerm(newDegree, newCoeff) 141 | 142 | # Advance the current pointer. 143 | curr = curr.next 144 | 145 | return newPoly 146 | 147 | # Helper method for appending terms to the polynomial 148 | def _appendTerm(self, degree, coefficient): 149 | if coefficient != 0.0: 150 | newTerm = _PolyTermNode(degree, coefficient) 151 | if self._polyHead is None: 152 | self._polyHead = newTerm 153 | else: 154 | self._polyTail.next = newTerm 155 | 156 | self._polyTail = newTerm 157 | 158 | # Class for creating polynomial term nodes used with the linked list. 159 | class _PolyTermNode(object): 160 | def __init__(self, degree, coefficient): 161 | self.degree = degree 162 | self.coefficient = coefficient 163 | self.next = None 164 | -------------------------------------------------------------------------------- /Chapter6/polynomial_test.py: -------------------------------------------------------------------------------- 1 | # Testing script for linked list implementation of polynomial ADT 2 | 3 | from polynomial import Polynomial 4 | 5 | def main(): 6 | print("Testing class constructor: ") 7 | eq1 = Polynomial() # Create empty polynomial object 8 | eq2 = Polynomial(2, 5) # Create polynomial object with degree 2 coefficient 5 9 | 10 | print("Testing degree method: ") 11 | print("Should be -1: ", eq1.degree()) 12 | print("Should be 2: ", eq2.degree()) 13 | 14 | print("Testing getitem method: ") 15 | # REVIEW: Not really sure how the getitem for this class works?? 16 | print(eq2[2]) 17 | 18 | print("Testing evaluate method: ") 19 | print(eq2.evaluate(4)) 20 | 21 | print("Testing simple_add method: ") 22 | eq2.simple_add(eq1) 23 | 24 | print("Testing add method: ") 25 | new_eq = eq1 + eq2 26 | print(new_eq) 27 | 28 | print("Testing sub method: ") 29 | # This method is not finished yet 30 | new_eq = eq1 - eq2 31 | print(new_eq) 32 | 33 | print("Testing mul method: ") 34 | # This method is not finished yet 35 | new_eq = eq1 * eq2 36 | print(new_eq) 37 | 38 | print("Testing multiply method: ") 39 | print(eq2.multiply(eq1)) 40 | 41 | 42 | if __name__ == '__main__': 43 | main() 44 | -------------------------------------------------------------------------------- /Chapter6/singlelinkedlist.py: -------------------------------------------------------------------------------- 1 | # Implementation of Singly Linked List using Node Class 2 | # TODO: Very Buggy Implementation, check the add method 3 | # REVIEW: Review the concept 4 | 5 | from node import Node 6 | 7 | class SinglyLinkedList: 8 | def __init__(self): 9 | self.head = None 10 | 11 | def checkOrder(self): 12 | """Checks if linked list is ordered or not: 13 | Returns Boolean""" 14 | pass 15 | 16 | def add(self, data): 17 | newNode = Node(data) 18 | if self.head == None: 19 | self.head = newNode 20 | else: 21 | newNode.next = self.head 22 | newNode.next.prev = newNode 23 | self.head = newNode 24 | 25 | def traversal(self): 26 | curNode = self.head 27 | while curNode is not None: 28 | print(curNode) 29 | curNode = curNode.next 30 | 31 | def unorderedSearch(self, target): 32 | curNode = self.head 33 | while curNode is not None and curNode.data != target: 34 | curNode = curNode.next 35 | return curNode is not None 36 | 37 | def orderedSearch(self, target): 38 | """Searches the target on the linked lists: 39 | """ 40 | if order != None: # If the checkOrder function is ran and we have order variable 41 | if order == True: 42 | # Complete the method 43 | pass 44 | else: 45 | print("orderedSearch is cannot be done in unordered Linked List") 46 | else: # If we did not check the Order yet, 47 | order = checkOrder() 48 | if order == True: 49 | # Complete the method 50 | pass 51 | else: 52 | print("orderedSearch is cannot be done in unordered Linked List") 53 | 54 | def remove(self, target): 55 | predNode = None 56 | curNode = self.head 57 | while curNode is not None and curNode.data != target: 58 | predNode = curNode 59 | curNode = curNode.next 60 | 61 | if curNode is not None: 62 | if curNode is self.head: 63 | self.head = curNode.next 64 | else: 65 | predNode.next = curNode.next 66 | 67 | def __str__( self ): 68 | # Simplfied printing by adding all the elements to string 69 | s = "" 70 | curNode = self.head 71 | if curNode != None : 72 | while curNode.next != None : 73 | s += curNode.data 74 | curNode = curNode.next 75 | s += curNode.data 76 | return ' '.join(list(s)) 77 | -------------------------------------------------------------------------------- /Chapter6/singlelinkedlist_test.py: -------------------------------------------------------------------------------- 1 | # Testing script to see every method and the class itself is functional 2 | from singlelinkedlist import SinglyLinkedList 3 | 4 | def main(): 5 | print("Testing class constructor:") 6 | SLL = SinglyLinkedList() 7 | 8 | print("Testing add method:") 9 | SLL.add(45) 10 | SLL.add(25) 11 | SLL.add(52) 12 | SLL.add(17) 13 | 14 | print("Testing traversal method:") 15 | SLL.traversal() 16 | 17 | print("Testing unorderedSearch method:") 18 | SLL.unorderedSearch(52) 19 | 20 | print("Testing remove method:") 21 | SLL.remove(17) 22 | 23 | 24 | if __name__ == '__main__': 25 | main() 26 | -------------------------------------------------------------------------------- /Chapter7/7-Stacks.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Stacks\n", 8 | "\n", 9 | "Stack is a type of container with restricted access that stores a linear collection. \n", 10 | "\n", 11 | "## The Stack ADT\n", 12 | "\n", 13 | "- The stack stores data in **last-in first-out** protocol\n", 14 | "- **Top** of the stack is where new items are added, or existing items are removed from the same end.\n", 15 | "- **Base** is the opposite end. \n", 16 | "\n", 17 | "![Stack ADT Visual](https://www.tutorialspoint.com/data_structures_algorithms/images/stack_representation.jpg)" 18 | ] 19 | }, 20 | { 21 | "cell_type": "code", 22 | "execution_count": null, 23 | "metadata": { 24 | "collapsed": true 25 | }, 26 | "outputs": [], 27 | "source": [] 28 | } 29 | ], 30 | "metadata": { 31 | "kernelspec": { 32 | "display_name": "Python 3", 33 | "language": "python", 34 | "name": "python3" 35 | }, 36 | "language_info": { 37 | "codemirror_mode": { 38 | "name": "ipython", 39 | "version": 3 40 | }, 41 | "file_extension": ".py", 42 | "mimetype": "text/x-python", 43 | "name": "python", 44 | "nbconvert_exporter": "python", 45 | "pygments_lexer": "ipython3", 46 | "version": "3.6.1" 47 | } 48 | }, 49 | "nbformat": 4, 50 | "nbformat_minor": 2 51 | } 52 | -------------------------------------------------------------------------------- /Chapter7/README.md: -------------------------------------------------------------------------------- 1 | ## Chapter 7: Stacks 2 | 3 | ### To Do List: 4 | 5 | - [ ] Finish chapter notes in ipynb 6 | - [ ] Define and Implement Stack ADT using Python Lists 7 | - [ ] Write a snippet to test Stack ADT using Python Lists 8 | - [ ] Define and Implement Stack ADT using Linked Lists 9 | - [ ] Write a snippet to test Stack ADT using Linked Lists 10 | - [ ] Implement Validation script for C/C++ source files 11 | - [ ] Define and Implement Maze ADT 12 | - [ ] Write a snippet to test Maze ADT 13 | - [ ] Write and Test a program that extracts postfix expressions from the user, evaluates the expression, and prints the results. 14 | - [ ] Complete the Validation script with all literal strings 15 | - [ ] Design and implement a function that evaluates a prefix expression stored as 16 | a text string. 17 | - [ ] Add findPath(), reset(), and draw() methods for the Maze class. 18 | - [ ] Modify the solve() method in Maze ADT to return a vector containing tuples representing the path through the maze. 19 | - [ ] Implement Postfix Calculator ADT 20 | - [ ] Extend the methods in Postfix Calculator ADT 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | {one line to give the program's name and a brief idea of what it does.} 635 | Copyright (C) {year} {name of author} 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | {project} Copyright (C) {year} {fullname} 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Data_Structures_and_Algorithms 2 | 3 | Prerequisites 4 | --- 5 | 6 | To be able to benefit fully from this guide you should have at least basic knowledge on the following Python domains: 7 | 8 | - Modules and namespaces in Python 9 | - Applying basic data types and constructs 10 | - Conditional statements 11 | - Loops 12 | - Functions 13 | - Built-in data structures; lists, dictionaries 14 | - Basic class and OOP in Python 15 | 16 | How to use this guide 17 | --- 18 | 19 | > Will be decided and added here 20 | 21 | 22 | 23 | Contents 24 | --- 25 | 26 | 1. [Data Structures and Algorithms Using Python](https://www.amazon.com/Data-Structures-Algorithms-Using-Python/dp/0470618299) 27 | - [Chapter 1: Abstract Data Types](https://github.com/eneskemalergin/Data_Structures_and_Algorithms/tree/master/Chapter1) 28 | - [Chapter 2: Arrays](https://github.com/eneskemalergin/Data_Structures_and_Algorithms/tree/master/Chapter2) 29 | - [Chapter 3: Sets and Maps](https://github.com/eneskemalergin/Data_Structures_and_Algorithms/tree/master/Chapter3) 30 | - [Chapter 4: Algorithm Analysis](https://github.com/eneskemalergin/Data_Structures_and_Algorithms/tree/master/Chapter4) 31 | - [Chapter 5: Searching and Sorting](https://github.com/eneskemalergin/Data_Structures_and_Algorithms/tree/master/Chapter5) 32 | - [Chapter 6: Linked Structures](https://github.com/eneskemalergin/Data_Structures_and_Algorithms/tree/master/Chapter6) 33 | - [Chapter 7: Stacks](https://github.com/eneskemalergin/Data_Structures_and_Algorithms/tree/master/Chapter7) 34 | - Chapter 8: Queues 35 | - Chapter 9: Advanced Linked Lists 36 | - Chapter 10: Recursion 37 | - Chapter 11: Hash Tables 38 | - Chapter 12: Advanced Sorting 39 | - Chapter 13: Binary Trees 40 | - Chapter 14: Search Trees 41 | - Appendix A: Python Review 42 | - Appendix B: User-Defined Modules 43 | - Appendix C: Exceptions 44 | - Appendix D: Classes 45 | 2. Book 2 (Coming Soon) 46 | --------------------------------------------------------------------------------