A => README.org +7 -0
@@ 1,7 @@
+* Runestone
+
+See https://runestone.academy/ns/books/published//pythonds/index.html
+
+These repos are my answers to the exercises in the book.
+
+Still need to look at the 8th and ninth chapter.
A => chapter1/README.org +6 -0
@@ 1,6 @@
+* Chapter 1
+
+Not much to do here, since I'm already confortable with python I chose
+to skip some of the exercises.
+
+And since I'm already doing the NAND book with the logic gates I got a bit lazy.
A => chapter1/fraction.py +46 -0
@@ 1,46 @@
+#!/usr/bin/python3
+
+
+"""
+The goal here is to override magic method for our Fraction object
+like gt,lt,sub,multiply,divide...
+"""
+
+def gcd(m,n):
+ while m%n != 0:
+ oldm = m
+ oldn = n
+
+ m = oldn
+ n = oldm%oldn
+ return n
+
+class Fraction:
+ def __init__(self,top,bottom):
+ self.num = top
+ self.den = bottom
+
+ def __str__(self):
+ return str(self.num)+"/"+str(self.den)
+
+ def show(self):
+ print(self.num,"/",self.den)
+
+ def __add__(self,otherfraction):
+ newnum = self.num*otherfraction.den + \
+ self.den*otherfraction.num
+ newden = self.den * otherfraction.den
+ common = gcd(newnum,newden)
+ return Fraction(newnum//common,newden//common)
+
+ def __eq__(self, other):
+ firstnum = self.num * other.den
+ secondnum = other.num * self.den
+
+ return firstnum == secondnum
+
+x = Fraction(1,2)
+y = Fraction(2,3)
+print(x+y)
+print(x == y)
+
A => chapter1/logic_gates.py +143 -0
@@ 1,143 @@
+#!/usr/bin/python3
+
+"""
+Create a two new gate classes, one called NorGate the other called
+NandGate. NandGates work like AndGates that have a Not attached to the
+output. NorGates work lake OrGates that have a Not attached to the
+output.
+
+Create a series of gates that prove the following equality NOT (( A
+and B) or (C and D)) is that same as NOT( A and B ) and NOT (C and
+D). Make sure to use some of your new gates in the simulation.
+"""
+
+class LogicGate:
+
+ def __init__(self,n):
+ self.name = n
+ self.output = None
+
+ def getLabel(self):
+ return self.name
+
+ def getOutput(self):
+ self.output = self.performGateLogic()
+ return self.output
+
+
+class BinaryGate(LogicGate):
+
+ def __init__(self,n):
+ super(BinaryGate, self).__init__(n)
+
+ self.pinA = None
+ self.pinB = None
+
+ def getPinA(self):
+ if self.pinA == None:
+ return int(input("Enter Pin A input for gate "+self.getLabel()+"-->"))
+ else:
+ return self.pinA.getFrom().getOutput()
+
+ def getPinB(self):
+ if self.pinB == None:
+ return int(input("Enter Pin B input for gate "+self.getLabel()+"-->"))
+ else:
+ return self.pinB.getFrom().getOutput()
+
+ def setNextPin(self,source):
+ if self.pinA == None:
+ self.pinA = source
+ else:
+ if self.pinB == None:
+ self.pinB = source
+ else:
+ print("Cannot Connect: NO EMPTY PINS on this gate")
+
+
+class AndGate(BinaryGate):
+
+ def __init__(self,n):
+ BinaryGate.__init__(self,n)
+
+ def performGateLogic(self):
+
+ a = self.getPinA()
+ b = self.getPinB()
+ if a==1 and b==1:
+ return 1
+ else:
+ return 0
+
+class OrGate(BinaryGate):
+
+ def __init__(self,n):
+ BinaryGate.__init__(self,n)
+
+ def performGateLogic(self):
+
+ a = self.getPinA()
+ b = self.getPinB()
+ if a ==1 or b==1:
+ return 1
+ else:
+ return 0
+
+class UnaryGate(LogicGate):
+
+ def __init__(self,n):
+ LogicGate.__init__(self,n)
+
+ self.pin = None
+
+ def getPin(self):
+ if self.pin == None:
+ return int(input("Enter Pin input for gate "+self.getLabel()+"-->"))
+ else:
+ return self.pin.getFrom().getOutput()
+
+ def setNextPin(self,source):
+ if self.pin == None:
+ self.pin = source
+ else:
+ print("Cannot Connect: NO EMPTY PINS on this gate")
+
+
+class NotGate(UnaryGate):
+
+ def __init__(self,n):
+ UnaryGate.__init__(self,n)
+
+ def performGateLogic(self):
+ if self.getPin():
+ return 0
+ else:
+ return 1
+
+
+class Connector:
+
+ def __init__(self, fgate, tgate):
+ self.fromgate = fgate
+ self.togate = tgate
+
+ tgate.setNextPin(self)
+
+ def getFrom(self):
+ return self.fromgate
+
+ def getTo(self):
+ return self.togate
+
+
+def main():
+ g1 = AndGate("G1")
+ g2 = AndGate("G2")
+ g3 = OrGate("G3")
+ g4 = NotGate("G4")
+ c1 = Connector(g1,g3)
+ c2 = Connector(g2,g3)
+ c3 = Connector(g3,g4)
+ print(g4.getOutput())
+
+main()
A => chapter1/monkey_type.py +83 -0
@@ 1,83 @@
+#!/usr/bin/python3
+
+"""The monkey that types a text if given an infinite amount of time."""
+
+import random
+import string
+
+
+def generate_phrase(best_phrase: str = None) -> str:
+ """Generate a random 28 string phrase."""
+ string_space = string.ascii_lowercase + " "
+ phrase = ""
+ if best_phrase:
+ phrase = best_phrase
+ while len(phrase) != 28:
+ letter = random.choice(string_space)
+ phrase = phrase + letter
+ return phrase
+
+
+def generate_from_best_phrase(best_phrase: str, score: float) -> str:
+ """Generate a phrase from the correct letters gathered so far."""
+ if score == 0:
+ generated_best_phrase = generate_phrase()
+ else:
+ generated_best_phrase = generate_phrase(best_phrase)
+ return generated_best_phrase
+
+
+def generate_score(
+ generated_string: str,
+ target: str
+) -> tuple[float, str]:
+ """Score the generated_phrase compared to the target"""
+ letters_matched = 0
+ letters_correct = ""
+ for letter_generated, letter_target in zip(generated_string, target):
+ if letter_generated == letter_target:
+ letters_matched += 1
+ letters_correct = letters_correct + letter_generated
+ else:
+ break
+ score = (letters_matched / len(generated_string)) * 100
+ return score, letters_correct
+
+
+def infinite_monkey():
+ """This should produce a phrase from Shakespeare given time."""
+ phrases_generated = {}
+ best_phrase = ""
+ best_score = 0.0
+ phrase = generate_phrase()
+ score, letters_correct = generate_score(
+ phrase,
+ "methinks it is like a weasel"
+ )
+ phrases_generated[phrase] = score
+ count = 0
+ while score != 100:
+ best_phrase = max(phrases_generated, key=phrases_generated.get)
+ best_score = phrases_generated[best_phrase]
+ phrase = generate_from_best_phrase(
+ letters_correct, score
+ )
+ score, letters_correct = generate_score(
+ phrase,
+ "methinks it is like a weasel"
+ )
+ if phrase not in phrases_generated:
+ phrases_generated[phrase] = score
+ if count % 1000 == 0:
+ print(f"{count} : {best_phrase} {best_score:.2f} %")
+ count += 1
+ print(f"{count} : {phrase} {score:.2f} %")
+
+
+def main():
+ """Entry point."""
+ infinite_monkey()
+
+
+if __name__ == '__main__':
+ main()
A => chapter2/README.org +4 -0
@@ 1,4 @@
+* Chapter 2
+
+This one mostly talks about override of standard methods, pitfalls
+and exercice about it
A => chapter2/dice.py +55 -0
@@ 1,55 @@
+#!/usr/bin/python3
+
+
+"""PYthon implements the negation of __eq__ if ne not implemented
+same with ohter operators"""
+
+import random
+
+class MSDie:
+ """
+ Multi-sided die
+
+ Instance Variables:
+ current_value
+ num_sides
+
+ """
+
+ def __init__(self, num_sides):
+ self.num_sides = num_sides
+ self.current_value = self.roll()
+
+ def roll(self):
+ self.current_value = random.randrange(1,self.num_sides+1)
+ return self.current_value
+
+ def __str__(self):
+ return str(self.current_value)
+
+ def __repr__(self):
+ return "MSDie({}) : {}".format(self.num_sides, self.current_value)
+
+ def __eq__(self,other):
+ return self.current_value == other.current_value
+
+ def __lt__(self,other):
+ return self.current_value < other.current_value
+
+ def __le__(self, other):
+ return self.current_value <= other.current_value
+
+
+x = MSDie(6)
+y = MSDie(7)
+
+x.current_value = 6
+y.current_value = 5
+
+print(x == y)
+print(x < y)
+print(x > y)
+print(x != y)
+print(x<=y)
+print(x>=y)
+print(x is y)
A => chapter3/README.org +59 -0
@@ 1,59 @@
+* Chapter 3
+
+This one 's about algorithm analysis
+
+The big O to compare algorith mregardless of their implementation in language,compiler,time...
+
+Although we do not see this in the summation example, sometimes the performance of an algorithm depends on the exact values of the data rather than simply the size of the problem. For these kinds of algorithms we need to characterize their performance in terms of best case, worst case, or average case performance. The worst case performance refers to a particular data set where the algorithm performs especially poorly. Whereas a different data set for the exact same algorithm might have extraordinarily good performance. However, in most cases the algorithm performs somewhere in between these two extremes (average case). It is important for a computer scientist to understand these distinctions so they are not misled by one particular case.
+
+a=5
+b=6
+c=10
+for i in range(n):
+ for j in range(n):
+ x = i * i
+ y = j * j
+ z = i * j
+for k in range(n):
+ w = a*k + 45
+ v = b*b
+d = 33
+
+The number of assignment operations is the sum of four terms. The first term is the constant 3, representing the three assignment statements at the start of the fragment. The second term is 3n2
+, since there are three statements that are performed n2 times due to the nested iteration. The third term is 2n, two statements iterated n times. Finally, the fourth term is the constant 1, representing the final assignment statement. This gives us T(n)=3+3n2+2n+1=3n2+2n+4. By looking at the exponents, we can easily see that the n2 term will be dominant and therefore this fragment of code is O(n2). Note that all of the other terms as well as the coefficient on the dominant term can be ignored as n grows larger.
+
+if nested loop
+o(n) to the power n
+
+if not nested
+o(n)
+
+see iflog(n) number of steps get reduced as algo goes on
+
+
+
+
+ Algorithm analysis is an implementation-independent way of measuring an algorithm.
+
+ Big-O notation allows algorithms to be classified by their dominant process with respect to the size of the problem.
+
+
+ how to use the timeit module, test function over big range, plot graphically to check time complexity
+
+ also depends on implementation for big oh
+
+
+import timeit
+import random
+
+for i in range(10000,1000001,20000):
+ t = timeit.Timer("random.randrange(%d) in x"%i,
+ "from __main__ import random,x")
+ x = list(range(i))
+ lst_time = t.timeit(number=1000)
+ x = {j:None for j in range(i)}
+ d_time = t.timeit(number=1000)
+ print("%d,%10.3f,%10.3f" % (i, lst_time, d_time))
+
+
+ Though this works only for small snippets, other tools for real project
A => chapter3/anagram.py +79 -0
@@ 1,79 @@
+#!/usr/bin/python3
+
+# n^2 solution
+
+def anagramSolution1(s1,s2):
+ stillOK = True
+ if len(s1) != len(s2):
+ stillOK = False
+
+ alist = list(s2)
+ pos1 = 0
+
+ while pos1 < len(s1) and stillOK:
+ pos2 = 0
+ found = False
+ while pos2 < len(alist) and not found:
+ if s1[pos1] == alist[pos2]:
+ found = True
+ else:
+ pos2 = pos2 + 1
+
+ if found:
+ alist[pos2] = None
+ else:
+ stillOK = False
+
+ pos1 = pos1 + 1
+
+ return stillOK
+
+print(anagramSolution1('abcd','dcba'))
+
+# depends on the sort
+
+def anagramSolution2(s1,s2):
+ alist1 = list(s1)
+ alist2 = list(s2)
+
+ alist1.sort()
+ alist2.sort()
+
+ pos = 0
+ matches = True
+
+ while pos < len(s1) and matches:
+ if alist1[pos]==alist2[pos]:
+ pos = pos + 1
+ else:
+ matches = False
+
+ return matches
+
+print(anagramSolution2('abcde','edcba'))
+
+# o(n), last loop is always 26
+
+def anagramSolution4(s1,s2):
+ c1 = [0]*26
+ c2 = [0]*26
+
+ for i in range(len(s1)):
+ pos = ord(s1[i])-ord('a')
+ c1[pos] = c1[pos] + 1
+
+ for i in range(len(s2)):
+ pos = ord(s2[i])-ord('a')
+ c2[pos] = c2[pos] + 1
+
+ j = 0
+ stillOK = True
+ while j<26 and stillOK:
+ if c1[j]==c2[j]:
+ j = j + 1
+ else:
+ stillOK = False
+
+ return stillOK
+
+print(anagramSolution4('apple','pleap'))
A => chapter3/check_performance.py +56 -0
@@ 1,56 @@
+#!/usr/bin/python3
+
+import random
+from timeit import Timer
+
+# popzero = Timer("x.pop(0)",
+# "from __main__ import x")
+# popend = Timer("x.pop()",
+# "from __main__ import x")
+# print("pop(0) pop()")
+# for i in range(1000000,100000001,1000000):
+# x = list(range(i))
+# pt = popend.timeit(number=1000)
+# x = list(range(i))
+# pz = popzero.timeit(number=1000)
+# print("%15.5f, %15.5f" %(pz,pt))
+
+
+# """Check that the index operation of list is O(1)"""
+# index_begin = Timer("x.index(0)",
+# "from __main__ import x")
+# index_last = Timer("x.index(x[-1])",
+# "from __main__ import x")
+# print("iteraton, index(0), index(last_element)")
+# for i in range(1000000,100000001,1000000):
+# x = list(range(i))
+# pt = index_begin.timeit(number=1000)
+# x = list(range(i))
+# pz = index_last.timeit(number=1000)
+# print("%d,%15.5f,%15.5f" % (i, pt, pz))
+
+
+# """Check that get and set item for dict are o(1)"""
+# print("Get dict time, Set dict element time")
+# for i in range(10000,1000001,20000):
+# t = Timer("x.get(%d)"%i,
+# "from __main__ import random,x")
+# t2 = Timer("x[(%d)] = 9999"%i,
+# "from __main__ import random,x")
+# x = {j:None for j in range(i)}
+# d_time1 = t.timeit(number=1000)
+# x = {j:None for j in range(i)}
+# d_time2 = t2.timeit(number=1000)
+# print("%d,%10.3f,%10.3f" % (i, d_time1, d_time2))
+
+# """Compare the del operator for list and dict"""
+# Should be o(1) for dict and o(n) for list
+print("iteration, del dict del list")
+for i in range(1000000,100000001,1000000):
+ del_timer = Timer("del x[random.randrange(%d)]"%i,
+ "from __main__ import random, x")
+ x = list(range(i))
+ pt = del_timer.timeit(number=1000)
+ x = {j:None for j in range(i)}
+ pz = del_timer.timeit(number=1000)
+ print("%d, %15.5f, %15.5f" %(i, pz,pt))
A => chapter3/exercises.py +15 -0
@@ 1,15 @@
+#!/usr/bin/python3
+
+# Devise an experiment to verify that the list index operator is O(1)
+
+# Devise an experiment to verify that get item and set item are O(1)
+
+# for dictionaries.
+
+# Devise an experiment that compares the performance of the del operator on lists and dictionaries.
+
+# Given a list of numbers in random order, write an algorithm that works in O(nlog(n))
+
+# to find the kth smallest number in the list.
+
+# Can you improve the algorithm from the previous problem to be linear? Explain.
A => chapter3/find_minimum.py +34 -0
@@ 1,34 @@
+#!/usr/bin/python3
+
+import random
+from typing import List
+
+def find_minimum(mylist: List[int]) -> int:
+ """This is o(n^2)"""
+ overallmin = mylist[0]
+ for i in mylist:
+ issmallest = True
+ for j in mylist:
+ if i > j:
+ issmallest = False
+ if issmallest:
+ overallmin = i
+ return overallmin
+
+
+def find_minimum_optimized(mylist: List[int]) -> int:
+ """This is o(n)"""
+ minsofar = mylist[0]
+ for i in mylist:
+ if i < minsofar:
+ minsofar = i
+ return minsofar
+
+
+def main():
+ test_list = [random.randint(0,10) for x in range(10)]
+ print(test_list)
+ print(find_minimum(test_list))
+
+if __name__ == '__main__':
+ main()
A => chapter4/README.org +10 -0
@@ 1,10 @@
+* Linear structures
+
+4.2. What Are Linear Structures?
+
+We will begin our study of data structures by considering four simple but very powerful concepts. Stacks, queues, deques, and lists are examples of data collections whose items are ordered depending on how they are added or removed. Once an item is added, it stays in that position relative to the other elements that came before and came after it. Collections such as these are often referred to as linear data structures.
+
+Linear structures can be thought of as having two ends. Sometimes these ends are referred to as the “left” and the “right” or in some cases the “front” and the “rear.” You could also call them the “top” and the “bottom.” The names given to the ends are not significant. What distinguishes one linear structure from another is the way in which items are added and removed, in particular the location where these additions and removals occur. For example, a structure might allow new items to be added at only one end. Some structures might allow items to be removed from either end.
+
+These variations give rise to some of the most useful data structures in computer science. They appear in many algorithms and can be used to solve a variety of important problems.
+
A => chapter4/dequeue.py +53 -0
@@ 1,53 @@
+#!/usr/bin/python3
+
+
+class Deque:
+ def __init__(self):
+ self.items = []
+
+ def isEmpty(self):
+ return self.items == []
+
+ def addFront(self, item):
+ self.items.append(item)
+
+ def addRear(self, item):
+ self.items.insert(0,item)
+
+ def removeFront(self):
+ return self.items.pop()
+
+ def removeRear(self):
+ return self.items.pop(0)
+
+ def size(self):
+ return len(self.items)
+
+ def __str__(self):
+ return str(self.items)
+
+
+def palchecker(aString: str) -> bool:
+ chardeque = Deque()
+
+ for ch in aString:
+ chardeque.addRear(ch)
+
+ stillEqual = True
+
+ while chardeque.size() > 1 and stillEqual:
+ first = chardeque.removeFront()
+ last = chardeque.removeRear()
+ if first == " ":
+ first = chardeque.removeFront()
+ if last == " ":
+ last = chardeque.removeRear()
+ if first != last:
+ stillEqual = False
+
+ return stillEqual
+
+
+print(palchecker("lsdkjfskf"))
+print(palchecker("radar"))
+print(palchecker('I PREFER PI'))
A => chapter4/list.py +642 -0
@@ 1,642 @@
+#!/usr/bin/python3
+
+class Node:
+ def __init__(self,initdata):
+ self.data = initdata
+ self.next = None
+
+ def getData(self):
+ return self.data
+
+ def getNext(self):
+ return self.next
+
+ def setData(self,newdata):
+ self.data = newdata
+
+ def setNext(self,newnext):
+ self.next = newnext
+
+ def __str__(self):
+ return str(self.data, str(self.next))
+
+
+class UnorderedList:
+
+ def __init__(self):
+ self.head = None
+ self.last = None
+
+ def isEmpty(self):
+ return self.head == None
+
+ def add(self,item):
+ temp = Node(item)
+ temp.setNext(self.head)
+ self.head = temp
+ if not self.last:
+ self.last = self.head
+
+ def size(self):
+ current = self.head
+ count = 0
+ while current != None:
+ count = count + 1
+ current = current.getNext()
+
+ return count
+
+ def search(self,item):
+ current = self.head
+ found = False
+ while current != None and not found:
+ if current.getData() == item:
+ found = True
+ else:
+ current = current.getNext()
+
+ return found
+
+ def remove(self,item) -> None:
+ current = self.head
+ previous = None
+ found = False
+ while not found:
+ if current.getData() == item:
+ found = True
+ else:
+ previous = current
+ current = current.getNext()
+ if current is None:
+ break
+ if previous is None:
+ self.head = current.getNext()
+ elif not found:
+ raise ValueError("The item is not in the list")
+ else:
+ previous.setNext(current.getNext())
+
+ def append(self, item) -> None:
+ """
+ We handle first the case where we append
+ to a list without any node by setting the
+ last node to the head.
+
+ When the list is not empty we simply set the
+ next pointer of the last node recorded to the given
+ node and finally we update the last node to be the given
+ node that is going to be appended.
+ """
+ temp = Node(item)
+ if not self.last:
+ self.head = temp
+ self.last = temp
+ else:
+ self.last.setNext(temp)
+ self.last = temp
+
+ def pop(self, pos=None):
+ """
+ Remove the last element and returns the element.
+
+ The last if else condition is to account for the case
+ where the list only has one node if there is no previous node
+ that means there is only one node and therefore the head should
+ be set to None.
+
+ Otherwise we change the last pointer to None
+ """
+ current = self.head
+ previous = None
+ while current:
+ if not current.getNext():
+ break
+ previous = current
+ current = current.getNext()
+ if not previous:
+ self.head = None
+ else:
+ previous.setNext(None)
+ return current.getData()
+
+ def insert(self, pos, item):
+ temp = Node(item)
+ current = self.head
+ count = 0
+ previous = None
+ while current:
+ if count == pos:
+ break
+ previous = current
+ current = current.getNext()
+ count += 1
+ if not current and pos == 0:
+ self.head = temp
+ else:
+ if not previous:
+ temp.setNext(current)
+ self.head = temp
+ else:
+ previous.setNext(temp)
+ temp.setNext(current)
+
+ def index(self, item) -> int:
+ """-1 to have list[0] == first element"""
+ index = -1
+ found = False
+ current = self.head
+ while current and not found:
+ if current.getData() == item:
+ found = True
+ else:
+ current = current.getNext()
+ index += 1
+ if not found:
+ index = -1
+ return index
+
+ def print_items(self) -> None:
+ current = self.head
+ while current:
+ print(current.getData())
+ current = current.getNext()
+
+ def slice(self):
+ pass
+
+ def __str__(self):
+ elements = ""
+ current = self.head
+ while current:
+ if current == self.head:
+ elements += "[" + str(current.getData())
+ else:
+ if current:
+ elements += ", "
+ elements += str(current.getData())
+ if not current.getNext():
+ elements += "]"
+ current = current.getNext()
+ return elements
+
+ def __getitem__(self, subscript):
+ if isinstance(subscript, slice):
+ # do your handling for a slice object:
+ count = 0
+ current = self.head
+ second_list = UnorderedList()
+ while count <= subscript.stop:
+ if count == subscript.stop:
+ count += 1
+ break
+ current = current.getNext()
+ count += 1
+ second_list.add(current.getData())
+ return second_list
+ else:
+ # Do your handling for a plain index
+ count = 0
+ current = self.head
+ second_list = UnorderedList()
+ while count != subscript:
+ if count == subscript:
+ count += 1
+ break
+ current = current.getNext()
+ count += 1
+ if not current:
+ raise IndexError("list index out of range")
+ second_list.add(current.getData())
+ return second_list
+
+#mylist = UnorderedList()
+#mylist.add(31)
+#mylist.add(32)
+#mylist.add(33)
+#mylist.add(38)
+#mylist.append(45)
+#print(mylist)
+#a = mylist[0:2]
+#print(a)
+#mylist.append(45)
+#mylist.print_items()
+#print('------------')
+#mylist.remove(45)
+#mylist.print_items()
+#mylist.add(33)
+#mylist.append(45)
+#mylist.print_items()
+##print(f"{mylist.pop()} pop result")
+#print(f"{mylist.pop(2)} pop result")
+#mylist.print_items()
+#mylist.add(93)
+#mylist.append(7524)
+#mylist.append(75245)
+#mylist.add(26)
+#mylist.append(752455458)
+#mylist.append(451)
+#mylist.print_items()
+#print(f"{mylist.index(752455458)} kjkljklj")
+#mylist.insert(0, 654687865445754)
+#mylist.insert(0, 54)
+#mylist.insert(1, 20)
+#mylist.print_items()
+# mylist.add(26)
+# mylist.add(54)
+# mylist.print_items()
+#
+# print(mylist.size())
+# print(mylist.search(93))
+# print(mylist.search(100))
+#
+# mylist.add(100)
+# print(mylist.search(100))
+# print(mylist.size())
+#
+# mylist.remove(54)
+# print(mylist.size())
+# mylist.remove(93)
+# print(mylist.size())
+# mylist.remove(31)
+# print(mylist.size())
+# print(mylist.search(93))
+
+
+# Ordered List
+
+class OrderedList:
+ def __init__(self):
+ self.head = None
+
+ def search(self,item):
+ current = self.head
+ found = False
+ stop = False
+ while current != None and not found and not stop:
+ if current.getData() == item:
+ found = True
+ else:
+ if current.getData() > item:
+ stop = True
+ else:
+ current = current.getNext()
+
+ return found
+
+ def add(self,item):
+ current = self.head
+ previous = None
+ stop = False
+ while current != None and not stop:
+ if current.getData() > item:
+ stop = True
+ else:
+ previous = current
+ current = current.getNext()
+
+ temp = Node(item)
+ if previous == None:
+ temp.setNext(self.head)
+ self.head = temp
+ else:
+ temp.setNext(current)
+ previous.setNext(temp)
+
+ def isEmpty(self):
+ return self.head == None
+
+ def size(self):
+ current = self.head
+ count = 0
+ while current != None:
+ count = count + 1
+ current = current.getNext()
+
+ return count
+
+ def remove(self, item) -> None:
+ current = self.head
+ previous = None
+ found = False
+ while not found:
+ if current.getData() == item:
+ found = True
+ else:
+ previous = current
+ current = current.getNext()
+
+ if previous == None:
+ self.head = current.getNext()
+ else:
+ previous.setNext(current.getNext())
+
+ def index(self, item) -> int:
+ """-1 to have list[0] == first element"""
+ index = -1
+ found = False
+ current = self.head
+ while current and not found:
+ if current.getData() == item:
+ found = True
+ else:
+ current = current.getNext()
+ index += 1
+ if not found:
+ index = -1
+ return index
+
+ def pop(self, pos=None):
+ """
+ Remove the last element and returns the element.
+
+ The last if else condition is to account for the case
+ where the list only has one node if there is no previous node
+ that means there is only one node and therefore the head should
+ be set to None.
+
+ Otherwise we change the last pointer to None
+ """
+ current = self.head
+ previous = None
+ while current:
+ if not current.getNext():
+ break
+ previous = current
+ current = current.getNext()
+ if not previous:
+ self.head = None
+ else:
+ previous.setNext(None)
+ return current.getData()
+
+
+ def print_items(self) -> None:
+ current = self.head
+ while current:
+ print(current.getData())
+ current = current.getNext()
+
+
+
+#mylist = OrderedList()
+#mylist.add(31)
+#mylist.add(77)
+#mylist.add(17)
+#mylist.add(93)
+#mylist.add(26)
+#mylist.add(54)
+#mylist.print_items()
+##
+##print(f"{mylist.pop()} pop result")
+#print(f"{mylist.index(93)} index result")
+#print("-----------------------")
+#mylist.print_items()
+
+# print(mylist.size())
+# print(mylist.search(93))
+# print(mylist.search(100))
+
+# Stack implemented as a linked list
+
+class StackLinkedList():
+
+ def __init__(self):
+ self.head = None
+ self.last = None
+
+ def isEmpty(self):
+ return self.head is None
+
+ def push(self,item):
+ temp = Node(item)
+ temp.setNext(self.head)
+ self.head = temp
+ if not self.last:
+ self.last = self.head
+
+ def pop(self):
+ current = self.head
+ if current.getNext() is not None:
+ self.head = current.getNext()
+ else:
+ self.head = None
+ return current.getData()
+
+ def peek(self):
+ if self.size() != 0:
+ return self.head.getData()
+
+ def size(self):
+ current = self.head
+ count = 0
+ while current is not None:
+ count = count + 1
+ current = current.getNext()
+
+ return count
+
+ def __str__(self):
+ elements = ""
+ current = self.head
+ while current:
+ if current == self.head:
+ elements += "[" + str(current.getData())
+ else:
+ if current:
+ elements += ", "
+ elements += str(current.getData())
+ if not current.getNext():
+ elements += "]"
+ current = current.getNext()
+ return elements
+
+
+#stack_list = StackLinkedList()
+#stack_list.push(10)
+#stack_list.push(20)
+#stack_list.push(30)
+#print(stack_list.peek())
+#print(stack_list)
+#print(stack_list.size())
+#print(stack_list.pop())
+#print(stack_list)
+#print(stack_list.peek())
+
+
+# Queue linked list
+
+class QueueLinkedList():
+
+ def __init__(self):
+ self.head = None
+ self.last = None
+
+ def isEmpty(self):
+ return self.head is None
+
+ def enqueue(self,item):
+ temp = Node(item)
+ temp.setNext(self.head)
+ self.head = temp
+ if not self.last:
+ self.last = self.head
+
+ def dequeue(self):
+ current = self.head
+ previous = None
+ return_value = None
+ if not current.getNext():
+ previous = current
+ while current:
+ previous = current
+ current = current.getNext()
+ if not current:
+ self.head = None
+ else:
+ previous.setNext(None)
+ return_value = previous.getData()
+ return return_value
+
+ def size(self):
+ current = self.head
+ count = 0
+ while current is not None:
+ count = count + 1
+ current = current.getNext()
+
+ return count
+
+ def __str__(self):
+ elements = ""
+ current = self.head
+ while current:
+ if current == self.head:
+ elements += "[" + str(current.getData())
+ else:
+ if current:
+ elements += ", "
+ elements += str(current.getData())
+ if not current.getNext():
+ elements += "]"
+ current = current.getNext()
+ return elements
+
+
+#queue_list = QueueLinkedList()
+#queue_list.enqueue(10)
+#queue_list.enqueue(20)
+#queue_list.enqueue(30)
+#print(queue_list)
+#print(queue_list.size())
+#print(queue_list.dequeue())
+#print(queue_list)
+
+# Dequeue linked list
+
+
+class DequeueLinkedList():
+
+ def __init__(self):
+ self.head = None
+ self.last = None
+
+ def isEmpty(self):
+ return self.head is None
+
+ def addFront(self,item):
+ temp = Node(item)
+ temp.setNext(self.head)
+ self.head = temp
+ if not self.last:
+ self.last = self.head
+
+ def addRear(self):
+ current = self.head
+ previous = None
+ return_value = None
+ if not current.getNext():
+ previous = current
+ while current:
+ previous = current
+ current = current.getNext()
+ if not current:
+ self.head = None
+ else:
+ previous.setNext(None)
+ return_value = previous.getData()
+ return return_value
+
+ def removeFront(self):
+ pass
+
+ def removeRear(self):
+ pass
+
+ def size(self):
+ current = self.head
+ count = 0
+ while current is not None:
+ count = count + 1
+ current = current.getNext()
+
+ return count
+
+ def __str__(self):
+ elements = ""
+ current = self.head
+ while current:
+ if current == self.head:
+ elements += "[" + str(current.getData())
+ else:
+ if current:
+ elements += ", "
+ elements += str(current.getData())
+ if not current.getNext():
+ elements += "]"
+ current = current.getNext()
+ return elements
+
+
+dequeue_list = DequeueLinkedList()
+dequeue_list.enqueue(10)
+dequeue_list.enqueue(20)
+dequeue_list.enqueue(30)
+
+# Doubly linked list
+# Head reference contains one to first node and one to last node
+
+class DoubleNode:
+ def __init__(self,initdata):
+ self.data = initdata
+ self.next = None
+ self.back = None
+
+ def getData(self):
+ return self.data
+
+ def getBack(self):
+ return self.back
+
+ def getNext(self):
+ return self.next
+
+ def setData(self,newdata):
+ self.data = newdata
+
+ def setBack(self, newback):
+ self.back = newback
+
+ def setNext(self,newnext):
+ self.next = newnext
+
+ def __str__(self):
+ return str(self.data, str(self.back), str(self.next))
+
+
+class DoublyLinkedList():
+ def __init__(self, args):
+ "docstring"
+
+
+
+# Implementation of queue that has o(1) in enqueue and o(1) in dequeue
A => chapter4/queue.py +178 -0
@@ 1,178 @@
+#!/usr/bin/python3
+
+from typing import List
+import random
+
+class Queue:
+ def __init__(self):
+ self.items = []
+
+ def isEmpty(self):
+ return self.items == []
+
+ def enqueue(self, item):
+ self.items.append(item)
+
+ def dequeue(self):
+ return self.items.pop(0)
+
+ def size(self):
+ return len(self.items)
+
+ def __str__(self):
+ return str(self.items)
+
+
+q = Queue()
+q.enqueue('hello')
+q.enqueue('dog')
+q.enqueue(3)
+q.dequeue()
+
+
+def hotPotato(namelist, num):
+ simqueue = Queue()
+ for name in namelist:
+ simqueue.enqueue(name)
+
+ while simqueue.size() > 1:
+ for i in range(random.randint(0,10)):
+ simqueue.enqueue(simqueue.dequeue())
+
+ simqueue.dequeue()
+
+ return simqueue.dequeue()
+
+# print(
+# hotPotato(
+# ["Bill","David","Susan","Jane","Kent","Brad"],7
+# )
+# )
+
+
+class Printer:
+ def __init__(self, ppm):
+ self.pagerate = ppm
+ self.currentTask = None
+ self.timeRemaining = 0
+
+ def tick(self):
+ if self.currentTask != None:
+ self.timeRemaining = self.timeRemaining - 1
+ if self.timeRemaining <= 0:
+ self.currentTask = None
+
+ def busy(self):
+ if self.currentTask != None:
+ return True
+ else:
+ return False
+
+ def startNext(self,newtask):
+ self.currentTask = newtask
+ # Divided by 2 because we supposed the average
+ # time got reduced
+ self.timeRemaining = (newtask.getPages() * 60/self.pagerate) / 2
+
+
+class Task:
+ def __init__(self,time):
+ self.timestamp = time
+ self.pages = random.randrange(1,21)
+
+ def getStamp(self):
+ return self.timestamp
+
+ def getPages(self):
+ return self.pages
+
+ def waitTime(self, currenttime):
+ return currenttime - self.timestamp
+
+
+def simulation(numSeconds, pagesPerMinute, numberOfStudents):
+
+ labprinter = Printer(pagesPerMinute)
+ printQueue = Queue()
+ waitingtimes = []
+
+ for currentSecond in range(numSeconds):
+
+ if newPrintTask(numberOfStudents):
+ task = Task(currentSecond)
+ printQueue.enqueue(task)
+
+ if (not labprinter.busy()) and (not printQueue.isEmpty()):
+ nexttask = printQueue.dequeue()
+ waitingtimes.append(nexttask.waitTime(currentSecond))
+ labprinter.startNext(nexttask)
+
+ labprinter.tick()
+
+ averageWait=sum(waitingtimes)/len(waitingtimes)
+ print("Average Wait %6.2f secs %3d tasks remaining."%(averageWait,printQueue.size()))
+
+def newPrintTask(numberOfStudents):
+ # The times 2 is because we assume every student uses
+ # the printer twice
+ task_average_per_hour = 3600 // (numberOfStudents * 2)
+ num = random.randrange(1, task_average_per_hour + 1)
+ if num == task_average_per_hour:
+ return True
+ else:
+ return False
+
+for i in range(10):
+ simulation(3600,5, 20)
+
+
+
+def radix10_sorting(numbers: List[str]) -> List[str]:
+ main_bin = Queue()
+
+ bin_0 = Queue()
+ bin_1 = Queue()
+ bin_2 = Queue()
+ bin_3 = Queue()
+ bin_4 = Queue()
+ bin_5 = Queue()
+ bin_6 = Queue()
+ bin_7 = Queue()
+ bin_8 = Queue()
+ bin_9 = Queue()
+
+ digit_bin = {
+ "0": bin_0,
+ "1": bin_1,
+ "2": bin_2,
+ "3": bin_3,
+ "4": bin_4,
+ "5": bin_5,
+ "6": bin_6,
+ "7": bin_7,
+ "8": bin_8,
+ "9": bin_9,
+ }
+
+ for num in numbers:
+ main_bin.enqueue(num)
+ reversed_digit = num[::-1]
+ for digit in reversed_digit:
+ digit_bin[digit].enqueue(main_bin.dequeue())
+
+ # Collect back the value into the main one
+ for d_bin in digit_bin.values():
+ if d_bin.size() != 0:
+ main_bin.enqueue(d_bin.dequeue())
+
+ queued_numbers: List[str] = []
+ while main_bin.size() != 0:
+ queued_numbers.append(main_bin.dequeue())
+
+ return queued_numbers
+
+
+#test_numbers = ["667", "534"]
+#test_numbers = ["2", "1"]
+#test_numbers = ["9", "3", "8", "1"]
+print(radix10_sorting(test_numbers))
A => chapter4/stack.py +292 -0
@@ 1,292 @@
+#!/usr/bin/python3
+
+"""
+With this structure it is easy
+to get the reverse order of the items inserted
+"""
+
+
+class Stack:
+ def __init__(self):
+ self.items = []
+
+ def isEmpty(self):
+ return self.items == []
+
+ def push(self, item):
+ self.items.append(item)
+
+ def pop(self):
+ return self.items.pop()
+
+ def peek(self):
+ return self.items[len(self.items)-1]
+
+ def size(self):
+ return len(self.items)
+
+ def __str__(self):
+ return str(self.items)
+
+
+m = Stack()
+m.push('x')
+m.push('y')
+m.push('z')
+while not m.isEmpty():
+ m.pop()
+
+test_string = "abc"
+
+def revstring(to_reverse: str) -> str:
+ reversed_string = ""
+ stack = Stack()
+ for letter in to_reverse:
+ stack.push(letter)
+ while not stack.isEmpty():
+ reversed_string = reversed_string + stack.peek()
+ stack.pop()
+ return reversed_string
+
+rev_string = revstring(test_string)
+print(rev_string)
+
+
+def parChecker(symbolString):
+ s = Stack()
+ balanced = True
+ index = 0
+ while index < len(symbolString) and balanced:
+ symbol = symbolString[index]
+ if symbol == "(":
+ s.push(symbol)
+ else:
+ if s.isEmpty():
+ balanced = False
+ else:
+ s.pop()
+
+ index = index + 1
+
+ if balanced and s.isEmpty():
+ return True
+ else:
+ return False
+
+print(parChecker('((()))'))
+print(parChecker('(()'))
+
+def parChecker(symbolString):
+ s = Stack()
+ balanced = True
+ index = 0
+ while index < len(symbolString) and balanced:
+ symbol = symbolString[index]
+ if symbol in "([{":
+ s.push(symbol)
+ else:
+ if s.isEmpty():
+ balanced = False
+ else:
+ top = s.pop()
+ if not matches(top,symbol):
+ balanced = False
+ index = index + 1
+ if balanced and s.isEmpty():
+ return True
+ else:
+ return False
+
+def matches(open,close):
+ opens = "([{"
+ closers = ")]}"
+ return opens.index(open) == closers.index(close)
+
+
+print(parChecker('{({([][])}())}'))
+print(parChecker('[{()]'))
+
+
+def divideBy2(decNumber):
+ remstack = Stack()
+
+ while decNumber > 0:
+ rem = decNumber % 2
+ remstack.push(rem)
+ decNumber = decNumber // 2
+
+ binString = ""
+ while not remstack.isEmpty():
+ binString = binString + str(remstack.pop())
+
+ return binString
+
+print(divideBy2(42))
+print(divideBy2(17))
+print(divideBy2(45))
+print(divideBy2(96))
+
+
+def baseConverter(decNumber,base):
+ digits = "0123456789ABCDEF"
+
+ remstack = Stack()
+
+ while decNumber > 0:
+ rem = decNumber % base
+ remstack.push(rem)
+ decNumber = decNumber // base
+
+ newString = ""
+ while not remstack.isEmpty():
+ newString = newString + digits[remstack.pop()]
+
+ return newString
+
+#print(baseConverter(25,2))
+#print(baseConverter(25,16))
+
+
+def matches(open,close):
+ opens = "<"
+ closers = ">"
+ return opens.index(open) == closers.index(close)
+
+
+def html_validator(symbolString):
+ opening_angle_bracket = Stack()
+ tags = Stack()
+ excluded_characters = ["\n", " "]
+ balanced = True
+ is_tag = True
+ current_token = ""
+ index = 0
+ while index < len(symbolString) and balanced:
+ symbol = symbolString[index]
+ if symbol in "<":
+ is_tag = True
+ opening_angle_bracket.push(symbol)
+ if symbol not in excluded_characters:
+ current_token += symbol
+ else:
+ if symbol in ">":
+ is_tag = False
+ if symbol not in excluded_characters:
+ current_token += symbol
+ if tags.size() !=0 and "</" + tags.peek()[1::] == current_token:
+ tags.pop()
+ else:
+ tags.push(current_token)
+ current_token = ""
+ top = opening_angle_bracket.pop()
+ else:
+ if symbol not in excluded_characters and is_tag:
+ current_token += symbol
+ index = index + 1
+ if balanced and opening_angle_bracket.isEmpty() and tags.isEmpty():
+ return True
+ else:
+ if not opening_angle_bracket.isEmpty():
+ print(f"Missing a closing '>' : {opening_angle_bracket.peek()}")
+ if not tags.isEmpty():
+ tag, tag_type = get_missing_tag(tags)
+ print(f"Missing a {tag_type} tag for : {tag}")
+ return False
+
+
+def get_missing_tag(tags: Stack) -> str:
+ tag = ""
+ tag_type = ""
+ while not tag:
+ if tags.size() > 0:
+ if "</" in tags.peek():
+ tags.pop()
+ else:
+ tag = tags.peek()
+ else:
+ break
+ if "</" in tag:
+ tag_type = "opening"
+ else:
+ tag_type = "closing"
+ return tag, tag_type
+
+
+test_html = """
+<html>
+ <title>
+ Example
+ </title>
+ </head>
+
+ <body>
+ <h1>Hello, world</h1>
+ </body>
+</html>
+"""
+
+print(html_validator(test_html))
+
+
+def infixToPostfix(infixexpr):
+ prec = {}
+ prec["*"] = 3
+ prec["/"] = 3
+ prec["+"] = 2
+ prec["-"] = 2
+ prec["("] = 1
+ opStack = Stack()
+ postfixList = []
+ tokenList = infixexpr.split()
+ breakpoint()
+ for token in tokenList:
+ if token in "ABCDEFGHIJKLMNOPQRSTUVWXYZ" or token in "0123456789":
+ postfixList.append(token)
+ elif token == '(':
+ opStack.push(token)
+ elif token == ')':
+ topToken = opStack.pop()
+ while topToken != '(':
+ postfixList.append(topToken)
+ topToken = opStack.pop()
+ else:
+ while (not opStack.isEmpty()) and \
+ (prec[opStack.peek()] >= prec[token]):
+ postfixList.append(opStack.pop())
+ opStack.push(token)
+
+ while not opStack.isEmpty():
+ postfixList.append(opStack.pop())
+ return " ".join(postfixList)
+
+print(infixToPostfix("( A * B ) + ( C * D )"))
+#print(infixToPostfix("( A + B ) * C - ( D - E ) * ( F + G )"))
+#print(infixToPostfix("( A + B ) * ( C + D ) * ( E + F )"))
+#print(infixToPostfix("A + ( ( B + C ) * ( D + E ) )"))
+#print(infixToPostfix("A * B * C * D + E + F"))
+
+def postfixEval(postfixExpr):
+ operandStack = Stack()
+ tokenList = postfixExpr.split()
+
+ for token in tokenList:
+ if token in "0123456789":
+ operandStack.push(int(token))
+ else:
+ operand2 = operandStack.pop()
+ operand1 = operandStack.pop()
+ result = doMath(token,operand1,operand2)
+ operandStack.push(result)
+ return operandStack.pop()
+
+def doMath(op, op1, op2):
+ if op == "*":
+ return op1 * op2
+ elif op == "/":
+ return op1 / op2
+ elif op == "+":
+ return op1 + op2
+ else:
+ return op1 - op2
+
+print(postfixEval('7 8 + 3 2 + /'))
A => chapter5/README.org +27 -0
@@ 1,27 @@
+* Recursion
+
+Recursion is a method of solving problems that involves breaking a problem down into smaller and smaller subproblems until you get to a small enough problem that it can be solved trivially. Usually recursion involves a function calling itself. While it may not seem like much on the surface, recursion allows us to write elegant solutions to problems that may otherwise be very difficult to program.
+
+
+* Stack frames
+
+Each time we make a call to toStr, we push a character on the stack. Returning to the previous example we can see that after the fourth call to toStr the stack would look like Figure 5. Notice that now we can simply pop the characters off the stack and concatenate them into the final result, "1010".
+
+
+#+DOWNLOADED: screenshot @ 2021-11-05 21:46:34
+[[file:Stack_frames/2021-11-05_21-46-34_screenshot.png]]
+
+
+The previous example gives us some insight into how Python implements a recursive function call. When a function is called in Python, a stack frame is allocated to handle the local variables of the function. When the function returns, the return value is left on top of the stack for the calling function to access. Figure 6 illustrates the call stack after the return statement on line 4.
+
+
+
+#+DOWNLOADED: screenshot @ 2021-11-05 21:45:54
+[[file:Recursion/2021-11-05_21-45-54_screenshot.png]]
+
+
+Figure 6: Call Stack Generated from toStr(10,2)
+
+Notice that the call to toStr(2//2,2) leaves a return value of "1" on the stack. This return value is then used in place of the function call (toStr(1,2)) in the expression "1" + convertString[2%2], which will leave the string "10" on the top of the stack. In this way, the Python call stack takes the place of the stack we used explicitly in Listing 4. In our list summing example, you can think of the return value on the stack taking the place of an accumulator variable.
+
+The stack frames also provide a scope for the variables used by the function. Even though we are calling the same function over and over, each call creates a new scope for the variables that are local to the function.
A => chapter5/Recursion/2021-11-05_21-45-54_screenshot.png +0 -0
A => chapter5/Stack_frames/2021-11-05_21-46-34_screenshot.png +0 -0
A => chapter5/base_converter.py +12 -0
@@ 1,12 @@
+#!/usr/bin/python3
+
+def toStr(n,base):
+ convertString = "0123456789ABCDEF"
+ if n < base:
+ return convertString[n]
+ else:
+ return toStr(n//base,base) + convertString[n%base]
+
+print(toStr(10,2))
+
+print(toStr(1453,16))
A => chapter5/euclid.py +26 -0
@@ 1,26 @@
+#!/usr/bin/python3
+
+def euclid(m: int, n: int) -> int:
+ remainder = m % n
+ while remainder != 0:
+ remainder = m % n
+ if remainder == 0:
+ return n
+ m, n = n, remainder
+ else:
+ return n
+
+
+def euclid_improved(m: int, n: int) -> int:
+ remainder = m % n
+ if remainder == 0:
+ return n
+ else:
+ return euclid_improved(n, remainder)
+
+
+if __name__ == '__main__':
+ m = int(input("Entrez un nombre entier positif m : "))
+ n = int(input("Entrez un nombre entier positif n : "))
+ result = euclid_improved(m, n)
+ print(f"Le PGCD entre {m} et {n} est {result}")
A => chapter5/factorial.py +81 -0
@@ 1,81 @@
+import string
+from typing import List, Dict
+
+def factorial(n: int) -> int:
+ if n <= 1:
+ return 1
+ else:
+ return n * factorial(n - 1)
+
+# print(factorial(5))
+# fac(5) -> 120: 5 * 4 * 3 * 2 * 1
+# fac(4) -> 24: 4 * 3 * 2 * 1
+# fac(3) -> 6 : 3 * 2 * 1
+# fac(2) -> 2 : 2 * 1
+# fac(1) -> 1 : 1
+# fac(0) -> 1
+
+
+def reverse_list(my_list: List[str | int]) -> List[str | int]:
+ if len(my_list) <= 1:
+ return my_list
+ else:
+ return my_list[-1::] + reverse_list(my_list[:-1])
+
+
+test_list = [10,20,30]
+test_list = [1,5,10,20,100,1000,10000]
+#print(reverse_list(test_list))
+
+
+def reverse_string(to_reverse: str) -> str:
+ if len(to_reverse) <= 1:
+ return to_reverse
+ else:
+ return to_reverse[-1] + reverse_string(to_reverse[:-1])
+
+
+test_string = "abc"
+#print(reverse_string(test_string))
+
+def is_palindrome(my_string: str) -> bool:
+ removed_punctuation = my_string.translate(
+ str.maketrans('', '', string.punctuation + '’')
+ )
+ my_string = ''.join(removed_punctuation.lower().split())
+ return bool(my_string == reverse_string(my_string))
+
+test_strings = [
+ "kayak",
+ "radar",
+ "aibohphobia",
+ "Live not on evil",
+ "Reviled did I live, said I, as evil I did deliver",
+ "Go hang a salami; I’m a lasagna hog.",
+ "Able was I ere I saw Elba",
+ "Kanakanak", # a town in Alaska,
+ "Wassamassaw", # a town in South Dakota
+ "madam i’m adam",
+ "hannah",
+ "hello",
+ "x",
+ ""
+]
+#for test_string in test_strings:
+# print(is_palindrome(test_string))
+
+# Fibonacci sequence
+# The Rule is xn = xn−1 + xn−2 or F_n is the sum of its 2 preceding number
+
+# 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, .
+
+def fibonacci(n: int, memo: Dict[int, int]) -> int:
+ if n <= 1:
+ return n
+ elif n in memo:
+ return memo[n]
+ else:
+ memo[n] = fibonacci(n - 1, memo) + fibonacci(n - 2, memo)
+ return memo[n]
+
+print(fibonacci(998, {0: 1,1: 1}))
A => chapter5/recursion.py +162 -0
@@ 1,162 @@
+#!/usr/bin/python3
+
+def listsum(numList):
+ theSum = 0
+ for i in numList:
+ theSum = theSum + i
+ return theSum
+
+print(listsum([1,3,5,7,9]))
+
+
+def listsum(numList):
+ if len(numList) == 1:
+ return numList[0]
+ else:
+ return numList[0] + listsum(numList[1:])
+
+print(listsum([1,3,5,7,9]))
+
+def toStr(n,base):
+ convertString = "0123456789ABCDEF"
+ if n < base:
+ return convertString[n]
+ else:
+ return toStr(n//base,base) + convertString[n%base]
+
+print(toStr(1453,16))
+
+
+def reverse(s):
+ return s
+
+testEqual(reverse("hello"),"olleh")
+testEqual(reverse("l"),"l")
+testEqual(reverse("follow"),"wollof")
+testEqual(reverse(""),"")
+
+from test import testEqual
+def removeWhite(s):
+ return s
+
+def isPal(s):
+ return False
+
+testEqual(isPal(removeWhite("x")),True)
+testEqual(isPal(removeWhite("radar")),True)
+testEqual(isPal(removeWhite("hello")),False)
+testEqual(isPal(removeWhite("")),True)
+testEqual(isPal(removeWhite("hannah")),True)
+testEqual(isPal(removeWhite("madam i'm adam")),True)
+
+rStack = Stack()
+
+def toStr(n,base):
+ convertString = "0123456789ABCDEF"
+ while n > 0:
+ if n < base:
+ rStack.push(convertString[n])
+ else:
+ rStack.push(convertString[n % base])
+ n = n // base
+ res = ""
+ while not rStack.isEmpty():
+ res = res + str(rStack.pop())
+ return res
+
+print(toStr(1453,16))
+
+
+import turtle
+
+myTurtle = turtle.Turtle()
+myWin = turtle.Screen()
+
+def drawSpiral(myTurtle, lineLen):
+ if lineLen > 0:
+ myTurtle.forward(lineLen)
+ myTurtle.right(90)
+ drawSpiral(myTurtle,lineLen-5)
+
+drawSpiral(myTurtle,100)
+myWin.exitonclick()
+
+import turtle
+
+def tree(branchLen,t):
+ if branchLen > 5:
+ t.forward(branchLen)
+ t.right(20)
+ tree(branchLen-15,t)
+ t.left(40)
+ tree(branchLen-15,t)
+ t.right(20)
+ t.backward(branchLen)
+
+def main():
+ t = turtle.Turtle()
+ myWin = turtle.Screen()
+ t.left(90)
+ t.up()
+ t.backward(100)
+ t.down()
+ t.color("green")
+ tree(75,t)
+ myWin.exitonclick()
+
+main()
+
+
+import turtle
+
+def drawTriangle(points,color,myTurtle):
+ myTurtle.fillcolor(color)
+ myTurtle.up()
+ myTurtle.goto(points[0][0],points[0][1])
+ myTurtle.down()
+ myTurtle.begin_fill()
+ myTurtle.goto(points[1][0],points[1][1])
+ myTurtle.goto(points[2][0],points[2][1])
+ myTurtle.goto(points[0][0],points[0][1])
+ myTurtle.end_fill()
+
+def getMid(p1,p2):
+ return ( (p1[0]+p2[0]) / 2, (p1[1] + p2[1]) / 2)
+
+def sierpinski(points,degree,myTurtle):
+ colormap = ['blue','red','green','white','yellow',
+ 'violet','orange']
+ drawTriangle(points,colormap[degree],myTurtle)
+ if degree > 0:
+ sierpinski([points[0],
+ getMid(points[0], points[1]),
+ getMid(points[0], points[2])],
+ degree-1, myTurtle)
+ sierpinski([points[1],
+ getMid(points[0], points[1]),
+ getMid(points[1], points[2])],
+ degree-1, myTurtle)
+ sierpinski([points[2],
+ getMid(points[2], points[1]),
+ getMid(points[0], points[2])],
+ degree-1, myTurtle)
+
+def main():
+ myTurtle = turtle.Turtle()
+ myWin = turtle.Screen()
+ myPoints = [[-100,-50],[0,100],[100,-50]]
+ sierpinski(myPoints,3,myTurtle)
+ myWin.exitonclick()
+
+main()
+
+def moveTower(height,fromPole, toPole, withPole):
+ if height >= 1:
+ moveTower(height-1,fromPole,withPole,toPole)
+ moveDisk(fromPole,toPole)
+ moveTower(height-1,withPole,toPole,fromPole)
+
+def moveDisk(fp,tp):
+ print("moving disk from",fp,"to",tp)
+
+moveTower(3,"A","B","C")
A => chapter5/stack_frame.py +20 -0
@@ 1,20 @@
+#!/usr/bin/python3
+
+from pythonds.basic import Stack
+
+rStack = Stack()
+
+def toStr(n,base):
+ convertString = "0123456789ABCDEF"
+ while n > 0:
+ if n < base:
+ rStack.push(convertString[n])
+ else:
+ rStack.push(convertString[n % base])
+ n = n // base
+ res = ""
+ while not rStack.isEmpty():
+ res = res + str(rStack.pop())
+ return res
+
+print(toStr(1453,16))
A => chapter5/tree.py +39 -0
@@ 1,39 @@
+#!/usr/bin/python3
+
+import turtle
+
+#myTurtle = turtle.Turtle()
+#myWin = turtle.Screen()
+
+def drawSpiral(myTurtle, lineLen):
+ if lineLen > 0:
+ myTurtle.forward(lineLen)
+ myTurtle.right(90)
+ drawSpiral(myTurtle,lineLen-5)
+
+#drawSpiral(myTurtle,100)
+#myWin.exitonclick()
+
+def tree(branchLen,t):
+ if branchLen > 5:
+ t.forward(branchLen)
+ t.right(20)
+ tree(branchLen-15,t)
+ breakpoint()
+ t.left(40)
+ tree(branchLen-15,t)
+ t.right(20)
+ t.backward(branchLen)
+
+def main():
+ t = turtle.Turtle()
+ myWin = turtle.Screen()
+ t.left(90)
+ t.up()
+ t.backward(100)
+ t.down()
+ t.color("green")
+ tree(30,t)
+ myWin.exitonclick()
+
+main()
A => chapter6/README.org +7 -0
@@ 1,7 @@
+* TODO
+
+- Heapsort
+
+- Prove that heapsort is o(n log n)
+
+- Optimal value for the hash resize, resize when the hash table is full at 50 75 % ? why ?
A => chapter6/binary_search.py +75 -0
@@ 1,75 @@
+#!/usr/bin/python3
+
+import random
+
+# def binarySearch(alist, item):
+# first = 0
+# last = len(alist)-1
+# found = False
+
+# while first<=last and not found:
+# midpoint = (first + last)//2
+# if alist[midpoint] == item:
+# found = True
+# else:
+# if item < alist[midpoint]:
+# last = midpoint-1
+# else:
+# first = midpoint+1
+
+# return found
+
+#testlist = [0, 1, 2, 8, 13, 17, 19, 32, 42]
+#print(binarySearch(testlist, 3))
+#print(binarySearch(testlist, 13))
+
+
+def binarySearch(alist, item):
+ if len(alist) == 0:
+ return False
+ else:
+ midpoint = len(alist)//2
+ if alist[midpoint]==item:
+ return True
+ else:
+ if item<alist[midpoint]:
+ return binarySearch(alist[:midpoint],item)
+ else:
+ return binarySearch(alist[midpoint+1:],item)
+
+#testlist = [0, 1, 2, 8, 13, 17, 19, 32, 42,]
+testlist = [3, 5, 6, 8, 11, 12, 14, 15, 17, 18]
+breakpoint()
+print(binarySearch(testlist, 16))
+#print(binarySearch(testlist, 3))
+#print(binarySearch(testlist, 13))
+
+# The same but without the slice
+
+def binarySearch(alist, first, last, item):
+ midpoint = (first + last) // 2
+ if first > last and item != alist[midpoint]:
+ return False
+ else:
+ if alist[midpoint] == item:
+ return True
+ else:
+ if item < alist[midpoint]:
+ return binarySearch(alist, first, midpoint - 1, item)
+ else:
+ return binarySearch(alist, midpoint + 1, last, item)
+
+#testlist = [0, 1, 2, 8, 13, 17, 19, 32, 42,]
+#testlist = [17, 46, 23, 56, 89, 86, 87, 16, 87, 65]
+# print(binarySearch(testlist, 0, len(testlist) - 1, 3))
+# print(binarySearch(testlist, 0, len(testlist) - 1, 13))
+# print(binarySearch(testlist, 0, len(testlist) - 1, 46))
+# [random.randint(10,100) for x in range(random.randint(5,10))]
+
+
+# When we split the list enough times, we end up with a list that has
+# just one item. Either that is the item we are looking for or it is
+# not. Either way, we are done. The number of comparisons necessary to
+# get to this point is i where n2i=1. Solving for i gives us i=logn. The
+# maximum number of comparisons is logarithmic with respect to the
+# number of items in the list. Therefore, the binary search is O(logn).
A => chapter6/bubble_sort.py +71 -0
@@ 1,71 @@
+#!/usr/bin/python3
+
+# The bubble sort makes multiple passes through a list. It compares
+# adjacent items and exchanges those that are out of order. Each pass
+# through the list places the next largest value in its proper place. In
+# essence, each item “bubbles” up to the location where it belongs.
+
+def bubbleSort(alist):
+ for passnum in range(len(alist) - 1, 0, -1):
+ for i in range(passnum):
+ if alist[i] > alist[i+1]:
+ alist[i+1], alist[i] = alist[i], alist[i+1]
+
+alist = [54,26,93,17,77,31,44,55,20]
+alist = [2,3,4,5,1]
+bubbleSort(alist)
+print(alist)
+
+# A bubble sort is often considered the most inefficient sorting method
+# since it must exchange items before the final location is known. These
+# “wasted” exchange operations are very costly. However, because the
+# bubble sort makes passes through the entire unsorted portion of the
+# list, it has the capability to do something most sorting algorithms
+# cannot. In particular, if during a pass there are no exchanges, then
+# we know that the list must be sorted. A bubble sort can be modified to
+# stop early if it finds that the list has become sorted. This means
+# that for lists that require just a few passes, a bubble sort may have
+# an advantage in that it will recognize the sorted list and
+# stop. ActiveCode 2 shows this modification, which is often referred to
+# as the short bubble.
+
+def shortBubbleSort(alist):
+ exchanges = True
+ passnum = len(alist)-1
+ while passnum > 0 and exchanges:
+ exchanges = False
+ for i in range(passnum):
+ if alist[i]>alist[i+1]:
+ exchanges = True
+ temp = alist[i]
+ alist[i] = alist[i+1]
+ alist[i+1] = temp
+ passnum = passnum-1
+
+# alist=[20,30,40,90,50,60,70,80,100,110]
+# shortBubbleSort(alist)
+# print(alist)
+
+# Alternative sort, this one makes less pass if there is like only one
+# item to move backwards
+
+def bubbleSortAlternating(alist):
+ for passnum in range(len(alist) -1, 0, -1):
+ if passnum % 2 == 0:
+ for i in range(passnum):
+ if alist[i] > alist[i+1]:
+ alist[i+1], alist[i] = alist[i], alist[i+1]
+ else:
+ for i in reversed(range(passnum)):
+ if alist[i] > alist[i+1]:
+ alist[i+1], alist[i] = alist[i], alist[i+1]
+ print(passnum)
+
+alist = [54,26,93,17,77,31,44,55,20]
+alist = [2,3,4,5,1]
+bubbleSortAlternating(alist)
+print(alist)
+
+# (2,3,4,5,1)
+# (2,3,4,1,5)
+# (1,2,3,4,5)
A => chapter6/hash.py +52 -0
@@ 1,52 @@
+#!/usr/bin/python3
+
+# Lambda (load factor) = number of items/ tablesize
+
+# 10% full
+# 25% full
+# 50% full
+# 75% full
+# 90% full
+# 99% full
+
+test_values = {0.10, 0.25, 0.50, 0.75, 0.90, 0.99}
+
+
+def succesful_probing(n: int) -> float:
+ avg_comparison = 1/2 * (1 + (1/1-n))
+ return avg_comparison
+
+
+def unsuccesful_probing(n: int) -> float:
+ avg_comparison = 1/2 * (1 + (1/1-n) ** 2)
+ return avg_comparison
+
+
+def succesful_chaining(n: int) -> float:
+ avg_comparison = 1 + n/2
+ return avg_comparison
+
+
+def unsuccesful_chaining(n: int) -> float:
+ avg_comparison = n
+ return avg_comparison
+
+
+print("Succesful probing")
+for count, n in enumerate(test_values, 1):
+ print(count, ": ", "load factor is : ", n, "average number of compaisons :",succesful_probing(n))
+
+
+print("Unsuccesful probing")
+for count, n in enumerate(test_values, 1):
+ print(count, ": ", "load factor is : ", n, "average number of compaisons :",unsuccesful_probing(n))
+
+
+print("Succesful chaining")
+for count, n in enumerate(test_values, 1):
+ print(count, ": ", "load factor is : ", n, "average number of compaisons :",succesful_chaining(n))
+
+
+print("Unsuccesful chaining")
+for count, n in enumerate(test_values, 1):
+ print(count, ": ", "load factor is : ", n, "average number of compaisons :", unsuccesful_chaining(n))
A => chapter6/hash_string.py +9 -0
@@ 1,9 @@
+#!/usr/bin/python3
+
+def hash(astring, tablesize):
+ sum = 0
+ for pos in range(len(astring)):
+ sum = sum + ord(astring[pos])
+
+ return sum%tablesize
+
A => chapter6/insertion_sort.py +24 -0
@@ 1,24 @@
+#!/usr/bin/python3
+
+# We begin by assuming that a list with one item (position 0) is already
+# sorted. On each pass, one for each item 1 through n−1, the current
+# item is checked against those in the already sorted sublist. As we
+# look back into the already sorted sublist, we shift those items that
+# are greater to the right. When we reach a smaller item or the end of
+# the sublist, the current item can be inserted.
+
+def insertionSort(alist):
+ for index in range(1, len(alist)):
+
+ currentvalue = alist[index]
+ position = index
+
+ while position > 0 and alist[position-1] > currentvalue:
+ alist[position] = alist[position-1]
+ position = position-1
+
+ alist[position] = currentvalue
+
+alist = [54, 26, 93, 17, 77, 31, 44, 55, 20]
+insertionSort(alist)
+print(alist)
A => chapter6/map.py +217 -0
@@ 1,217 @@
+#!/usr/bin/python3
+
+class HashTable:
+ def __init__(self):
+ self.size = 11
+ self.slots = [None] * self.size
+ self.data = [None] * self.size
+ self.load = 0
+ self.load_factor = 0
+
+ def put(self, key, data):
+ # Check the capacity of the map
+ if self.load_factor >= 75:
+ growth_factor = 10
+ self.size = self.size + growth_factor
+ # Need to recalculate the hash for the elements that were in the slots
+ old_slots = self.slots
+ old_datas = self.data
+ self.slots = [None] * self.size
+ self.data = [None] * self.size
+ self.load = 0
+ self.load_factor = int((self.load / self.size) * 100)
+ for old_slot, old_data in zip(old_slots, old_datas):
+ if old_slot is not None:
+ self.put(old_slot, old_data)
+
+ hashvalue = self.hashfunction(key, len(self.slots))
+
+ if self.slots[hashvalue] is None:
+ self.slots[hashvalue] = key
+ self.data[hashvalue] = data
+ self.load += 1
+ self.load_factor = int((self.load / self.size) * 100)
+ else:
+ if self.slots[hashvalue] == key:
+ self.data[hashvalue] = data #replace
+ else:
+ nextslot = self.rehash(hashvalue,len(self.slots))
+ while self.slots[nextslot] is not None and \
+ self.slots[nextslot] != key:
+ nextslot = self.rehash(nextslot, len(self.slots))
+ if self.slots[nextslot] is None:
+ self.slots[nextslot] = key
+ self.data[nextslot] = data
+ self.load += 1
+ self.load_factor = int((self.load / self.size) * 100)
+ else:
+ self.data[nextslot] = data #replace
+
+ def hashfunction(self, key, size):
+ return key % size
+
+ def rehash(self, oldhash, size):
+ return (oldhash+1) % size
+
+ def get(self, key):
+ startslot = self.hashfunction(key, len(self.slots))
+
+ data = None
+ stop = False
+ found = False
+ position = startslot
+ while self.slots[position] != None and \
+ not found and not stop:
+ if self.slots[position] == key:
+ found = True
+ data = self.data[position]
+ else:
+ position=self.rehash(position,len(self.slots))
+ if position == startslot:
+ stop = True
+ return data
+
+ def __getitem__(self,key):
+ return self.get(key)
+
+ def __setitem__(self,key,data):
+ self.put(key,data)
+
+ def __len__(self):
+ return len([x for x in self.slots if x is not None])
+
+ def __contains__(self, key):
+ # Could also use a try except indexoutofbonds?
+ # that's something that is done for iteration with StopIteration
+ return key in self.slots
+
+ def __delitem__(self, key):
+ startslot = self.hashfunction(key, len(self.slots))
+ stop = False
+ found = False
+ position = startslot
+ while self.slots[position] != None and \
+ not found and not stop:
+ if self.slots[position] == key:
+ found = True
+ else:
+ position=self.rehash(position,len(self.slots))
+ if position == startslot:
+ stop = True
+ del self.slots[position]
+ del self.data[position]
+ self.load -= 1
+ self.load_factor = int((self.load / self.size) * 100)
+
+
+H=HashTable()
+H[54]="cat"
+H[26]="dog"
+H[93]="lion"
+H[17]="tiger"
+H[77]="bird"
+H[31]="cow"
+H[44]="goat"
+H[55]="pig"
+H[20]="chicken"
+print(H.slots)
+print(H.data)
+
+print(H[20])
+
+print(H[17])
+H[20]='duck'
+print(H[20])
+print(H[99])
+print(len(H))
+print(20 in H)
+
+# Filling to capacity
+H[104]="Creeper"
+H[158]="chicken"
+H[24]="turtle"
+H[25]="eagle"
+H[15]="blue eyes white dragon"
+print(H.slots)
+print(H.data)
+print(H[104])
+print(len(H))
+del H[104]
+
+
+class HashTableQuadratic(HashTable):
+ def __init__(self):
+ super().__init__()
+ self.quadratic = 1
+
+ def put(self, key, data):
+ # Check the capacity of the map
+ if self.load_factor >= 75:
+ growth_factor = 10
+ self.size = self.size + growth_factor
+ # Need to recalculate the hash for the elements that were in the slots
+ old_slots = self.slots
+ old_datas = self.data
+ self.slots = [None] * self.size
+ self.data = [None] * self.size
+ self.load = 0
+ self.load_factor = int((self.load / self.size) * 100)
+ for old_slot, old_data in zip(old_slots, old_datas):
+ if old_slot is not None:
+ self.put(old_slot, old_data)
+
+ hashvalue = self.hashfunction(key, len(self.slots))
+
+ if self.slots[hashvalue] is None:
+ self.slots[hashvalue] = key
+ self.data[hashvalue] = data
+ self.load += 1
+ self.load_factor = int((self.load / self.size) * 100)
+ else:
+ if self.slots[hashvalue] == key:
+ self.data[hashvalue] = data #replace
+ else:
+ self.quadratic = 1
+ nextslot = self.rehash(hashvalue,len(self.slots))
+ while self.slots[nextslot] is not None and \
+ self.slots[nextslot] != key:
+ nextslot = self.rehash(nextslot, len(self.slots))
+ if self.slots[nextslot] is None:
+ self.slots[nextslot] = key
+ self.data[nextslot] = data
+ self.load += 1
+ self.load_factor = int((self.load / self.size) * 100)
+ else:
+ self.data[nextslot] = data #replace
+
+ def rehash(self, oldhash, size):
+ newhash = oldhash + self.quadratic ** self.quadratic
+ self.quadratic += 1
+ if newhash > self.size - 1:
+ return newhash % self.size
+ return newhash
+
+J = HashTableQuadratic()
+J[54]="cat"
+J[26]="dog"
+J[93]="lion"
+J[17]="tiger"
+J[77]="bird"
+J[31]="cow"
+J[44]="goat"
+J[55]="pig"
+J[20]="chicken"
+
+print(J.slots)
+print(J.data)
+print(len(J))
+
+#J[545]="pig"
+#J[207]="chicken"
+#
+#
+#print(J.slots)
+#print(J.data)
+#print(len(J))
+#
+#(54,26,93,17,77,31,44,55,20)
A => chapter6/merge_sort.py +38 -0
@@ 1,38 @@
+#!/usr/bin/python3
+
+def mergeSort(alist):
+ print("Splitting ",alist)
+ if len(alist)>1:
+ mid = len(alist)//2
+ lefthalf = alist[:mid]
+ righthalf = alist[mid:]
+
+ mergeSort(lefthalf)
+ mergeSort(righthalf)
+
+ i=0
+ j=0
+ k=0
+ while i < len(lefthalf) and j < len(righthalf):
+ if lefthalf[i] <= righthalf[j]:
+ alist[k]=lefthalf[i]
+ i=i+1
+ else:
+ alist[k]=righthalf[j]
+ j=j+1
+ k=k+1
+
+ while i < len(lefthalf):
+ alist[k]=lefthalf[i]
+ i=i+1
+ k=k+1
+
+ while j < len(righthalf):
+ alist[k]=righthalf[j]
+ j=j+1
+ k=k+1
+ print("Merging ",alist)
+
+alist = [54,26,93,17,77,31,44,55,20]
+mergeSort(alist)
+print(alist)
A => chapter6/quicksort.py +56 -0
@@ 1,56 @@
+#!/usr/bin/python3
+
+from typing import List
+
+def quickSort(alist):
+ quickSortHelper(alist, 0, len(alist)-1)
+
+def quickSortHelper(alist, first, last):
+ if first < last:
+ splitpoint = partition(alist, first, last)
+
+ quickSortHelper(alist, first, splitpoint-1)
+ quickSortHelper(alist, splitpoint+1, last)
+
+
+def partition(alist, first, last):
+ pivotvalue = alist[first]
+
+ leftmark = first+1
+# leftmark = len(alist) // 2
+ rightmark = last
+
+ done = False
+ while not done:
+
+ while leftmark <= rightmark and alist[leftmark] <= pivotvalue:
+ leftmark = leftmark + 1
+
+ while alist[rightmark] >= pivotvalue and rightmark >= leftmark:
+ rightmark = rightmark -1
+
+ if rightmark < leftmark:
+ done = True
+ else:
+ temp = alist[leftmark]
+ alist[leftmark] = alist[rightmark]
+ alist[rightmark] = temp
+
+ #temp = alist[]
+ temp = alist[first]
+ alist[first] = alist[rightmark]
+ #alist[first] = alist[rightmark]
+ alist[rightmark] = temp
+
+ return rightmark
+
+def median_of_three(alist: List[int]) -> int:
+ return 1
+
+#alist = [54, 26]
+#alist = [17, 20, 26, 31, 44, 54, 55, 77, 93]
+#alist = [54, 26, 93, 17, 77, 31, 44, 55, 20]
+alist = [14, 17, 13, 15, 19, 10, 3, 16, 9, 12]
+breakpoint()
+quickSort(alist)
+print(alist)
A => chapter6/selection_sort.py +31 -0
@@ 1,31 @@
+#!/usr/bin/python3
+
+# The selection sort improves on the bubble sort by making only one
+# exchange for every pass through the list. In order to do this, a
+# selection sort looks for the largest value as it makes a pass and,
+# after completing the pass, places it in the proper location. As with a
+# bubble sort, after the first pass, the largest item is in the correct
+# place. After the second pass, the next largest is in place. This
+# process continues and requires n−1 passes to sort n items, since the
+# final item must be in place after the (n−1)st pass.
+
+# Figure 3 shows the entire sorting process. On each pass, the largest
+# remaining item is selected and then placed in its proper location. The
+# first pass places 93, the second pass places 77, the third places 55,
+# and so on. The function is shown in ActiveCode 1.
+
+def selectionSort(alist):
+ for fillslot in range(len(alist) - 1, 0, -1):
+ positionOfMax = 0
+ for location in range(1, fillslot+1):
+ if alist[location] > alist[positionOfMax]:
+ positionOfMax = location
+
+ alist[fillslot], alist[positionOfMax] = (
+ alist[positionOfMax], alist[fillslot]
+ )
+
+alist = [54,26,93,17,77,31,44,55,20]
+breakpoint()
+selectionSort(alist)
+print(alist)
A => chapter6/sequential_search.py +49 -0
@@ 1,49 @@
+#!/usr/bin/python3
+
+def sequentialSearch(alist, item):
+ pos = 0
+ found = False
+
+ while pos < len(alist) and not found:
+ if alist[pos] == item:
+ found = True
+ else:
+ pos = pos+1
+
+ return found
+
+testlist = [1, 2, 32, 8, 17, 19, 42, 13, 0]
+print(sequentialSearch(testlist, 3))
+print(sequentialSearch(testlist, 13))
+print(sequentialSearch("abc", "z"))
+
+# Complexity o(n), case where item is present and not present, avg case
+
+def orderedSequentialSearch(alist, item):
+ pos = 0
+ found = False
+ stop = False
+ while pos < len(alist) and not found and not stop:
+ if alist[pos] == item:
+ found = True
+ else:
+ if alist[pos] > item:
+ stop = True
+ else:
+ pos = pos+1
+
+ return found
+
+testlist = [0, 1, 2, 8, 13, 17, 19, 32, 42,]
+print(orderedSequentialSearch(testlist, 3))
+print(orderedSequentialSearch(testlist, 13))
+
+# With this one there is less comparison to do if the item is not present
+# e.g, for a list of integer
+
+# summarizes these results. Note that in the best case we might discover
+# that the item is not in the list by looking at only one item. On
+# average, we will know after looking through only n2 items. However,
+# this technique is still O(n). In summary, a sequential search is
+# improved by ordering the list only in the case where we do not find
+# the item.
A => chapter6/shell_sort.py +28 -0
@@ 1,28 @@
+#!/usr/bin/python3
+
+def shellSort(alist):
+ sublistcount = len(alist)//2
+ while sublistcount > 0:
+
+ for startposition in range(sublistcount):
+ gapInsertionSort(alist, startposition, sublistcount)
+
+ print("After increments of size", sublistcount, "The list is", alist)
+
+ sublistcount = sublistcount // 2
+
+def gapInsertionSort(alist, start, gap):
+ for i in range(start+gap, len(alist), gap):
+
+ currentvalue = alist[i]
+ position = i
+
+ while position >= gap and alist[position-gap] > currentvalue:
+ alist[position] = alist[position-gap]
+ position = position-gap
+
+ alist[position] = currentvalue
+
+alist = [54, 26, 93, 17, 77, 31, 44, 55, 20]
+shellSort(alist)
+print(alist)
A => chapter7/README.org +3 -0
@@ 1,3 @@
+* Chapter 7
+
+- Do the priority Queue
A => chapter7/avl.py +60 -0
@@ 1,60 @@
+#!/usr/bin/python3
+
+def _put(self,key,val,currentNode):
+ if key < currentNode.key:
+ if currentNode.hasLeftChild():
+ self._put(key,val,currentNode.leftChild)
+ else:
+ currentNode.leftChild = TreeNode(key,val,parent=currentNode)
+ self.updateBalance(currentNode.leftChild)
+ else:
+ if currentNode.hasRightChild():
+ self._put(key,val,currentNode.rightChild)
+ else:
+ currentNode.rightChild = TreeNode(key,val,parent=currentNode)
+ self.updateBalance(currentNode.rightChild)
+
+def updateBalance(self,node):
+ if node.balanceFactor > 1 or node.balanceFactor < -1:
+ self.rebalance(node)
+ return
+ if node.parent != None:
+ if node.isLeftChild():
+ node.parent.balanceFactor += 1
+ elif node.isRightChild():
+ node.parent.balanceFactor -= 1
+
+ if node.parent.balanceFactor != 0:
+ self.updateBalance(node.parent)
+
+def rotateLeft(self,rotRoot):
+ newRoot = rotRoot.rightChild
+ rotRoot.rightChild = newRoot.leftChild
+ if newRoot.leftChild != None:
+ newRoot.leftChild.parent = rotRoot
+ newRoot.parent = rotRoot.parent
+ if rotRoot.isRoot():
+ self.root = newRoot
+ else:
+ if rotRoot.isLeftChild():
+ rotRoot.parent.leftChild = newRoot
+ else:
+ rotRoot.parent.rightChild = newRoot
+ newRoot.leftChild = rotRoot
+ rotRoot.parent = newRoot
+ rotRoot.balanceFactor = rotRoot.balanceFactor + 1 - min(newRoot.balanceFactor, 0)
+ newRoot.balanceFactor = newRoot.balanceFactor + 1 + max(rotRoot.balanceFactor, 0)
+
+def rebalance(self,node):
+ if node.balanceFactor < 0:
+ if node.rightChild.balanceFactor > 0:
+ self.rotateRight(node.rightChild)
+ self.rotateLeft(node)
+ else:
+ self.rotateLeft(node)
+ elif node.balanceFactor > 0:
+ if node.leftChild.balanceFactor < 0:
+ self.rotateLeft(node.leftChild)
+ self.rotateRight(node)
+ else:
+ self.rotateRight(node)
A => chapter7/binary_heap.py +221 -0
@@ 1,221 @@
+#!/usr/bin/python3
+
+"""Min heap implementation to starts with."""
+
+class BinHeap:
+ def __init__(self):
+ self.heapList = [0]
+ self.currentSize = 0
+ self.heapSize = 3
+
+ def percUp(self,i):
+ while i // 2 > 0:
+ if self.heapList[i] < self.heapList[i // 2]:
+ tmp = self.heapList[i // 2]
+ self.heapList[i // 2] = self.heapList[i]
+ self.heapList[i] = tmp
+ i = i // 2
+
+ def insert(self,k):
+ self.heapList.append(k)
+ self.currentSize = self.currentSize + 1
+ self.percUp(self.currentSize)
+
+ def percDown(self,i):
+ while (i * 2) <= self.currentSize:
+ mc = self.minChild(i)
+ if self.heapList[i] > self.heapList[mc]:
+ tmp = self.heapList[i]
+ self.heapList[i] = self.heapList[mc]
+ self.heapList[mc] = tmp
+ i = mc
+
+ def minChild(self,i):
+ if i * 2 + 1 > self.currentSize:
+ return i * 2
+ else:
+ if self.heapList[i*2] < self.heapList[i*2+1]:
+ return i * 2
+ else:
+ return i * 2 + 1
+
+ def delMin(self):
+ retval = self.heapList[1]
+ self.heapList[1] = self.heapList[self.currentSize]
+ self.currentSize = self.currentSize - 1
+ self.heapList.pop()
+ self.percDown(1)
+ return retval
+
+ def findMin(self):
+ return self.heapList[1]
+
+ def isEmpty(self):
+ return bool(len(self.heapList) <= 1)
+
+ def size(self):
+ return len(self.heapList[1:])
+
+ def buildHeap(self,alist):
+ i = len(alist) // 2
+ self.currentSize = len(alist)
+ self.heapList = [0] + alist[:]
+ while (i > 0):
+ self.percDown(i)
+ i = i - 1
+
+ def __str__(self):
+ return str(self.heapList)
+
+bh = BinHeap()
+#bh.buildHeap(
+# [5, 9, 11, 14, 18, 19, 21, 33, 17, 27]
+#)
+bh.buildHeap([9,5,6,2,3])
+print(bh.heapList)
+
+# print(bh.delMin())
+# print(bh.delMin())
+# print(bh.delMin())
+# print(bh.delMin())
+# print(bh.delMin())
+
+# bh.insert(5)
+# bh.insert(9)
+# bh.insert(11)
+# bh.insert(14)
+# bh.insert(18)
+# bh.insert(19)
+# bh.insert(21)
+# bh.insert(33)
+# bh.insert(17)
+# bh.insert(27)
+
+# print(bh.delMin())
+# print(bh.delMin())
+# print(bh.delMin())
+# print(bh.delMin())
+# print(bh.delMin())
+# print(bh.delMin())
+# print(bh.delMin())
+# print(bh.delMin())
+# print(bh.delMin())
+# print(bh.delMin())
+
+
+# Max heap
+
+# Should get 9,6,5,3,2
+
+class maxHeap(BinHeap):
+
+ def findMax(self):
+ return self.heapList[1]
+
+ def percUp(self,i):
+ while i // 2 > 0:
+ if self.heapList[i] >= self.heapList[i // 2]:
+ tmp = self.heapList[i // 2]
+ self.heapList[i // 2] = self.heapList[i]
+ self.heapList[i] = tmp
+ i = i // 2
+
+ def insert(self,k):
+ self.heapList.append(k)
+ self.currentSize = self.currentSize + 1
+ self.percUp(self.currentSize)
+
+ def delMax(self):
+ retval = self.heapList[1]
+ self.heapList[1] = self.heapList[self.currentSize // 2]
+ self.currentSize = self.currentSize - 1
+ self.heapList.pop(1)
+ i = 2 if self.currentSize % 2 == 0 and self.currentSize > 0 else 1
+ self.percUp(i)
+ return retval
+
+ def maxChild(self,i):
+ if i * 2 + 1 > self.currentSize:
+ return i * 2
+ else:
+ if self.heapList[i*2] >= self.heapList[i*2+1]:
+ return i * 2
+ else:
+ return i * 2 + 1
+
+ def percDown(self,i):
+ while (i * 2) <= self.currentSize:
+ mc = self.maxChild(i)
+ if self.heapList[i] <= self.heapList[mc]:
+ tmp = self.heapList[i]
+ self.heapList[i] = self.heapList[mc]
+ self.heapList[mc] = tmp
+ i = mc
+
+ def buildHeap(self,alist):
+ i = len(alist) // 2
+ self.currentSize = len(alist)
+ self.heapList = [0] + alist
+ while (i > 0):
+ self.percDown(i)
+ i = i - 1
+
+mh = maxHeap()
+breakpoint()
+mh.buildHeap(
+ [5, 9, 11, 14, 18, 19, 21, 33, 17, 27]
+)
+#mh.buildHeap([9,5,6,2,3])
+print(mh.heapList)
+# # mh.insert(9)
+# # mh.insert(5)
+# # mh.insert(6)
+# # mh.insert(2)
+# # mh.insert(3)
+
+# mh.insert(5)
+# mh.insert(9)
+# mh.insert(11)
+# mh.insert(14)
+# mh.insert(18)
+# mh.insert(19)
+# mh.insert(21)
+# mh.insert(33)
+# mh.insert(17)
+# mh.insert(27)
+
+print(mh.delMax())
+print(mh.delMax())
+print(mh.delMax())
+print(mh.delMax())
+print(mh.delMax())
+print(mh.delMax())
+print(mh.delMax())
+print(mh.delMax())
+print(mh.delMax())
+print(mh.delMax())
+
+
+
+class PriorityQueue(BinHeap):
+
+ def enqueue(self, value):
+ self.insert(value)
+
+ def dequeue(self):
+ return self.heapList.pop(1)
+
+
+# priority_queue = PriorityQueue()
+# priority_queue.enqueue(9)
+# priority_queue.enqueue(1)
+# priority_queue.enqueue(8)
+# priority_queue.enqueue(2)
+
+# print(priority_queue.dequeue())
+# print(priority_queue.dequeue())
+
+
+class heapSort():
+ """Using the buildHeap method, write a sorting function that can sort a list in O(n log(n)) time."""
+ pass
A => chapter7/binary_search_tree.py +241 -0
@@ 1,241 @@
+#!/usr/bin/python3
+
+class TreeNode:
+ def __init__(self,key,val,left=None,right=None,parent=None):
+ self.key = key
+ self.payload = val
+ self.leftChild = left
+ self.rightChild = right
+ self.parent = parent
+
+ def hasLeftChild(self):
+ return self.leftChild
+
+ def hasRightChild(self):
+ return self.rightChild
+
+ def isLeftChild(self):
+ return self.parent and self.parent.leftChild == self
+
+ def isRightChild(self):
+ return self.parent and self.parent.rightChild == self
+
+ def isRoot(self):
+ return not self.parent
+
+ def isLeaf(self):
+ return not (self.rightChild or self.leftChild)
+
+ def hasAnyChildren(self):
+ return self.rightChild or self.leftChild
+
+ def hasBothChildren(self):
+ return self.rightChild and self.leftChild
+
+ def spliceOut(self):
+ if self.isLeaf():
+ if self.isLeftChild():
+ self.parent.leftChild = None
+ else:
+ self.parent.rightChild = None
+ elif self.hasAnyChildren():
+ if self.hasLeftChild():
+ if self.isLeftChild():
+ self.parent.leftChild = self.leftChild
+ else:
+ self.parent.rightChild = self.leftChild
+ self.leftChild.parent = self.parent
+ else:
+ if self.isLeftChild():
+ self.parent.leftChild = self.rightChild
+ else:
+ self.parent.rightChild = self.rightChild
+ self.rightChild.parent = self.parent
+
+ def findSuccessor(self):
+ succ = None
+ if self.hasRightChild():
+ succ = self.rightChild.findMin()
+ else:
+ if self.parent:
+ if self.isLeftChild():
+ succ = self.parent
+ else:
+ self.parent.rightChild = None
+ succ = self.parent.findSuccessor()
+ self.parent.rightChild = self
+ return succ
+
+ def findMin(self):
+ current = self
+ while current.hasLeftChild():
+ current = current.leftChild
+ return current
+
+ def replaceNodeData(self,key,value,lc,rc):
+ self.key = key
+ self.payload = value
+ self.leftChild = lc
+ self.rightChild = rc
+ if self.hasLeftChild():
+ self.leftChild.parent = self
+ if self.hasRightChild():
+ self.rightChild.parent = self
+
+ def __iter__(self):
+ if self:
+ if self.hasLeftChild():
+ for elem in self.leftChiLd:
+ yield elem
+ yield self.key
+ if self.hasRightChild():
+ for elem in self.rightChild:
+ yield elem
+
+
+
+class BinarySearchTree:
+
+ def __init__(self):
+ self.root = None
+ self.size = 0
+
+ def length(self):
+ return self.size
+
+ def __len__(self):
+ return self.size
+
+ def put(self,key,val):
+ if self.root:
+ self._put(key,val,self.root)
+ else:
+ self.root = TreeNode(key,val)
+ self.size = self.size + 1
+
+ def _put(self,key,val,currentNode):
+ if key < currentNode.key:
+ if currentNode.hasLeftChild():
+ self._put(key,val,currentNode.leftChild)
+ else:
+ currentNode.leftChild = TreeNode(key,val,parent=currentNode)
+ elif key == currentNode.key:
+ currentNode.payload = val
+ else:
+ if currentNode.hasRightChild():
+ self._put(key,val,currentNode.rightChild)
+ else:
+ currentNode.rightChild = TreeNode(key,val,parent=currentNode)
+
+ def __setitem__(self,k,v):
+ self.put(k,v)
+
+ def get(self,key):
+ if self.root:
+ res = self._get(key,self.root)
+ if res:
+ return res.payload
+ else:
+ return None
+ else:
+ return None
+
+ def _get(self,key,currentNode):
+ if not currentNode:
+ return None
+ elif currentNode.key == key:
+ return currentNode
+ elif key < currentNode.key:
+ return self._get(key,currentNode.leftChild)
+ else:
+ return self._get(key,currentNode.rightChild)
+
+ def __getitem__(self,key):
+ return self.get(key)
+
+ def __contains__(self,key):
+ if self._get(key,self.root):
+ return True
+ else:
+ return False
+
+ def delete(self,key):
+ if self.size > 1:
+ nodeToRemove = self._get(key,self.root)
+ if nodeToRemove:
+ self.remove(nodeToRemove)
+ self.size = self.size-1
+ else:
+ raise KeyError('Error, key not in tree')
+ elif self.size == 1 and self.root.key == key:
+ self.root = None
+ self.size = self.size - 1
+ else:
+ raise KeyError('Error, key not in tree')
+
+ def __delitem__(self,key):
+ self.delete(key)
+
+ def remove(self,currentNode):
+ if currentNode.isLeaf(): #leaf
+ if currentNode == currentNode.parent.leftChild:
+ currentNode.parent.leftChild = None
+ else:
+ currentNode.parent.rightChild = None
+ elif currentNode.hasBothChildren(): #interior
+ succ = currentNode.findSuccessor()
+ succ.spliceOut()
+ currentNode.key = succ.key
+ currentNode.payload = succ.payload
+
+ else: # this node has one child
+ if currentNode.hasLeftChild():
+ if currentNode.isLeftChild():
+ currentNode.leftChild.parent = currentNode.parent
+ currentNode.parent.leftChild = currentNode.leftChild
+ elif currentNode.isRightChild():
+ currentNode.leftChild.parent = currentNode.parent
+ currentNode.parent.rightChild = currentNode.leftChild
+ else:
+ currentNode.replaceNodeData(currentNode.leftChild.key,
+ currentNode.leftChild.payload,
+ currentNode.leftChild.leftChild,
+ currentNode.leftChild.rightChild)
+ else:
+ if currentNode.isLeftChild():
+ currentNode.rightChild.parent = currentNode.parent
+ currentNode.parent.leftChild = currentNode.rightChild
+ elif currentNode.isRightChild():
+ currentNode.rightChild.parent = currentNode.parent
+ currentNode.parent.rightChild = currentNode.rightChild
+ else:
+ currentNode.replaceNodeData(currentNode.rightChild.key,
+ currentNode.rightChild.payload,
+ currentNode.rightChild.leftChild,
+ currentNode.rightChild.rightChild)
+
+
+
+
+mytree = BinarySearchTree()
+mytree[3]="red"
+mytree[4]="blue"
+mytree[6]="yellow"
+mytree[2]="at"
+
+print(mytree[6])
+print(mytree[2])
+print(mytree[3])
+print(mytree[4])
+
+print("----------------")
+
+# Overriding the second slot
+
+mytree[2]="ataz"
+
+print(mytree[6])
+print(mytree[2])
+print(mytree[3])
+print(mytree[4])
+
A => chapter7/parse_tree.py +94 -0
@@ 1,94 @@
+#!/usr/bin/python3
+
+import operator
+from pythonds.basic import Stack
+from pythonds.trees import BinaryTree
+
+
+def buildParseTree(fpexp: str) -> BinaryTree:
+ fplist = []
+ for token in fpexp:
+ is_last_token_number = False
+ is_cur_token_number = False
+ try:
+ is_cur_token_number = isinstance(int(token), int)
+ except ValueError:
+ pass
+ try:
+ if len(fplist) > 0:
+ is_last_token_number = bool(int(fplist[-1]))
+ except ValueError:
+ pass
+ if token == ' ':
+ continue
+ elif len(fplist) > 0 and is_last_token_number and is_cur_token_number:
+ fplist[-1] = fplist[-1] + token
+ else:
+ fplist.append(token)
+
+ pStack = Stack()
+ eTree = BinaryTree('')
+ pStack.push(eTree)
+ currentTree = eTree
+
+ for i in fplist:
+ if i == '(':
+ currentTree.insertLeft('')
+ pStack.push(currentTree)
+ currentTree = currentTree.getLeftChild()
+
+ elif i in ['+', '-', '*', '/']:
+ currentTree.setRootVal(i)
+ currentTree.insertRight('')
+ pStack.push(currentTree)
+ currentTree = currentTree.getRightChild()
+
+ elif i == ')':
+ currentTree = pStack.pop()
+
+ elif i not in ['+', '-', '*', '/', ')']:
+ try:
+ currentTree.setRootVal(int(i))
+ parent = pStack.pop()
+ currentTree = parent
+
+ except ValueError:
+ raise ValueError("token '{}' is not a valid integer".format(i))
+
+ return eTree
+
+
+pt = buildParseTree("( ( 10 + 5 ) * 3 )")
+pt2 = buildParseTree("((10+5)*3)")
+pt3 = buildParseTree("((14897+4874)*3)")
+pt.postorder() # defined and explained in the next section
+
+def evaluate(parseTree: BinaryTree):
+ """Evaluate a parse tree."""
+ opers = {
+ '+': operator.add,
+ '-': operator.sub,
+ '*': operator.mul,
+ '/': operator.truediv,
+ ' ': operator.or_,
+ ' ': operator.and_,
+ ' ': operator.is_not,
+ }
+
+ leftC = parseTree.getLeftChild()
+ rightC = parseTree.getRightChild()
+
+ if leftC and rightC:
+ fn = opers[parseTree.getRootVal()]
+ return fn(evaluate(leftC), evaluate(rightC))
+ else:
+ return parseTree.getRootVal()
+
+
+print(evaluate(pt))
+print(evaluate(pt2))
+print(evaluate(pt3))
+
+
+def calc_derivative(parseTree: BinaryTree) -> int:
+ pass
A => chapter7/tree_aslist.py +60 -0
@@ 1,60 @@
+#!/usr/bin/python3
+
+def BinaryTree(r):
+ return [r, [], []]
+
+def insertLeft(root,newBranch):
+ t = root.pop(1)
+ if len(t) > 1:
+ root.insert(1,[newBranch,t,[]])
+ else:
+ root.insert(1,[newBranch, [], []])
+ return root
+
+def insertRight(root,newBranch):
+ t = root.pop(2)
+ if len(t) > 1:
+ root.insert(2,[newBranch,[],t])
+ else:
+ root.insert(2,[newBranch,[],[]])
+ return root
+
+def getRootVal(root):
+ return root[0]
+
+def setRootVal(root,newVal):
+ root[0] = newVal
+
+def getLeftChild(root):
+ return root[1]
+
+def getRightChild(root):
+ return root[2]
+
+r = BinaryTree(3)
+insertLeft(r,4)
+insertLeft(r,5)
+insertRight(r,6)
+insertRight(r,7)
+l = getLeftChild(r)
+print(l)
+
+setRootVal(l,9)
+print(r)
+insertLeft(l,11)
+print(r)
+print(getRightChild(getRightChild(r)))
+
+
+def build_tree():
+ r = BinaryTree("a")
+ insertLeft(r,'b')
+ left = getLeftChild(r)
+ insertRight(left, 'd')
+ insertRight(r,'c')
+ right = getRightChild(r)
+ insertLeft(right,'e')
+ insertRight(right,'f')
+ return r
+
+tree = build_tree()
A => chapter7/tree_asrefererence.py +61 -0
@@ 1,61 @@
+#!/usr/bin/python3
+
+class BinaryTree:
+ def __init__(self,rootObj):
+ self.key = rootObj
+ self.leftChild = None
+ self.rightChild = None
+
+ def insertLeft(self,newNode):
+ if self.leftChild == None:
+ self.leftChild = BinaryTree(newNode)
+ else:
+ t = BinaryTree(newNode)
+ t.leftChild = self.leftChild
+ self.leftChild = t
+
+ def insertRight(self,newNode):
+ if self.rightChild == None:
+ self.rightChild = BinaryTree(newNode)
+ else:
+ t = BinaryTree(newNode)
+ t.rightChild = self.rightChild
+ self.rightChild = t
+
+
+ def getRightChild(self):
+ return self.rightChild
+
+ def getLeftChild(self):
+ return self.leftChild
+
+ def setRootVal(self,obj):
+ self.key = obj
+
+ def getRootVal(self):
+ return self.key
+
+
+r = BinaryTree('a')
+print(r.getRootVal())
+print(r.getLeftChild())
+r.insertLeft('b')
+print(r.getLeftChild())
+print(r.getLeftChild().getRootVal())
+r.insertRight('c')
+print(r.getRightChild())
+print(r.getRightChild().getRootVal())
+r.getRightChild().setRootVal('hello')
+print(r.getRightChild().getRootVal())
+
+def build_tree():
+ r = BinaryTree('a')
+ r.insertLeft('b')
+ r.insertRight('c')
+ r.getLeftChild().insertRight('d')
+ r.getRightChild().insertLeft('e')
+ r.getRightChild().insertRight('f')
+ return r
+
+tree = build_tree()
+print(tree)
A => chapter7/tree_traversal.py +84 -0
@@ 1,84 @@
+#!/usr/bin/python3
+
+from pythonds.trees import BinaryTree
+import operator
+
+def preorder(tree):
+ if tree:
+ print(tree.getRootVal())
+ preorder(tree.getLeftChild())
+ preorder(tree.getRightChild())
+
+
+def preorder(self):
+ print(self.key)
+ if self.leftChild:
+ self.leftChild.preorder()
+ if self.rightChild:
+ self.rightChild.preorder()
+
+
+def postorder(tree):
+ if tree != None:
+ postorder(tree.getLeftChild())
+ postorder(tree.getRightChild())
+ print(tree.getRootVal())
+
+def postordereval(tree):
+ opers = {'+':operator.add, '-':operator.sub, '*':operator.mul, '/':operator.truediv}
+ res1 = None
+ res2 = None
+ if tree:
+ res1 = postordereval(tree.getLeftChild())
+ res2 = postordereval(tree.getRightChild())
+ if res1 and res2:
+ return opers[tree.getRootVal()](res1,res2)
+ else:
+ return tree.getRootVal()
+
+
+def inorder(tree):
+ if tree != None:
+ inorder(tree.getLeftChild())
+ print(tree.getRootVal())
+ inorder(tree.getRightChild())
+
+
+def printexp(tree):
+ sVal = ""
+ if tree:
+ if tree.getLeftChild() is None:
+ sVal = printexp(tree.getLeftChild())
+ else:
+ sVal = '(' + printexp(tree.getLeftChild())
+ sVal = sVal + str(tree.getRootVal())
+ if tree.getLeftChild() is None:
+ sVal = sVal + printexp(tree.getRightChild())
+ else:
+ sVal = sVal + printexp(tree.getRightChild())+')'
+ return sVal
+
+# Tree traversals examples
+
+r = BinaryTree('a')
+r.insertLeft('b')
+r.insertRight('c')
+r.getRightChild().insertRight('helloRight')
+r.getLeftChild().insertLeft('helloLeft')
+
+inorder(r)
+
+# Evaluating expressions
+
+r = BinaryTree('*')
+r.insertLeft('+')
+r.getLeftChild().insertLeft(10)
+r.getLeftChild().insertRight(20)
+r.insertRight(2)
+
+# (((10)+(20))*(2)) -> ((10+20)*2)
+
+
+print(postordereval(r))
+
+print(printexp(r))
A => chapter8/graph.py +73 -0
@@ 1,73 @@
+#!/usr/bin/python3
+
+
+class Vertex:
+ def __init__(self,key):
+ self.id = key
+ self.connectedTo = {}
+
+ def addNeighbor(self,nbr,weight=0):
+ self.connectedTo[nbr] = weight
+
+ def getConnections(self):
+ return self.connectedTo.keys()
+
+ def getId(self):
+ return self.id
+
+ def getWeight(self,nbr):
+ return self.connectedTo[nbr]
+
+ def __str__(self):
+ return str(self.id) + ' connectedTo: ' + str([x.id for x in self.connectedTo])
+
+
+class Graph:
+ def __init__(self):
+ self.vertList = {}
+ self.numVertices = 0
+
+ def addVertex(self,key):
+ self.numVertices = self.numVertices + 1
+ newVertex = Vertex(key)
+ self.vertList[key] = newVertex
+ return newVertex
+
+ def addEdge(self,f,t,weight=0):
+ if f not in self.vertList:
+ nv = self.addVertex(f)
+ if t not in self.vertList:
+ nv = self.addVertex(t)
+ self.vertList[f].addNeighbor(self.vertList[t], weight)
+
+ def getVertex(self,n):
+ if n in self.vertList:
+ return self.vertList[n]
+ else:
+ return None
+
+ def getVertices(self):
+ return self.vertList.keys()
+
+ def __contains__(self,n):
+ return n in self.vertList
+
+ def __iter__(self):
+ return iter(self.vertList.values())
+
+
+g = Graph()
+for i in range(6):
+ g.addVertex(i)
+print(g.vertList)
+g.addEdge(0,5,2)
+g.addEdge(1,2,4)
+g.addEdge(2,3,9)
+g.addEdge(3,4,7)
+g.addEdge(3,5,3)
+g.addEdge(4,0,1)
+g.addEdge(5,4,8)
+g.addEdge(5,2,1)
+for v in g:
+ for w in v.getConnections():
+ print("( %s , %s )" % (v.getId(), w.getId()))
A => chapter8/word_ladder.py +56 -0
@@ 1,56 @@
+#!/usr/bin/python3
+
+from pythonds.graphs import Graph
+
+def buildGraph(wordFile):
+ d = {}
+ g = Graph()
+ wfile = open(wordFile,'r')
+ # create buckets of words that differ by one letter
+ for line in wfile:
+ word = line[:-1]
+ for i in range(len(word)):
+ bucket = word[:i] + '_' + word[i+1:]
+ if bucket in d:
+ d[bucket].append(word)
+ else:
+ d[bucket] = [word]
+ # add vertices and edges for words in the same bucket
+ for bucket in d.keys():
+ for word1 in d[bucket]:
+ for word2 in d[bucket]:
+ if word1 != word2:
+ g.addEdge(word1,word2)
+ return g
+
+
+
+from pythonds.graphs import Graph, Vertex
+from pythonds.basic import Queue
+
+def bfs(g,start):
+ start.setDistance(0)
+ start.setPred(None)
+ vertQueue = Queue()
+ vertQueue.enqueue(start)
+ while (vertQueue.size() > 0):
+ currentVert = vertQueue.dequeue()
+ for nbr in currentVert.getConnections():
+ if (nbr.getColor() == 'white'):
+ nbr.setColor('gray')
+ nbr.setDistance(currentVert.getDistance() + 1)
+ nbr.setPred(currentVert)
+ vertQueue.enqueue(nbr)
+ currentVert.setColor('black')
+
+
+def traverse(y):
+ x = y
+ while (x.getPred()):
+ print(x.getId())
+ x = x.getPred()
+ print(x.getId())
+
+
+g = buildGraph()
+traverse(g.getVertex('sage'))