├── README.md ├── chap1-Introduction ├── Card.py ├── Deck.py ├── Fraction.py ├── FractionClient.py ├── Programming_Exercises.md ├── SudokuTest.py ├── WarCardGame.py ├── gcd.py └── sudoku.py ├── chap2-AlgoAnalysis ├── Anagram.py ├── Anagram_N.py ├── Anagram_N_inout.txt ├── Anagram_inout.txt ├── FindKMin.py ├── FindKMin_inout.txt ├── FindMin.py ├── FindMin_inout.txt ├── Programming_Exercises_2.md ├── SumOfN.py ├── SumOfN_Run.txt ├── del_dictlist.py ├── del_dictlist_inout.txt ├── dict_getset.py ├── dict_getset_input.txt ├── list_index.py ├── list_index_input.txt ├── timer.py └── timer_inout.txt ├── chap3-BasicDataStructure ├── BalancedSymbols.py ├── BaseConverter.py ├── Calculator.py ├── Calculator_inout.txt ├── DecToBin.py ├── Deque.py ├── DequeLinkedList.py ├── DequeLinkedListClient.py ├── DoublyLinkedList.py ├── DoublyLinkedListClient.py ├── HTMLTagValidator.py ├── HTMLTagValidator_inout.txt ├── HotPotato.py ├── HotPotatoRand.py ├── HotPotatoRand_inout.txt ├── InfixCalc.py ├── InfixCalc_inout.txt ├── InfixToPostfix.py ├── InfixToPostfix_inout.txt ├── LinkedList.py ├── LinkedListClient.py ├── Node.py ├── OrderedList.py ├── OrderedListClient.py ├── Palindrome.py ├── ParenChecker.py ├── PostfixEval.py ├── Programming_Exercises_chap3.md ├── QClient.py ├── QNode.py ├── Queue.py ├── QueueADT.py ├── QueueADTClient.py ├── QueueADTClient_inout.txt ├── QueueLinkedList.py ├── QueueLinkedListClient.py ├── RadixSort.py ├── RadixSort_inout.txt ├── SLList.py ├── SLListClient.py ├── Stack.py ├── StackClient.py ├── StackLinkedList.py ├── StackLinkedListClient.py ├── Tokenizer.py ├── UnorderedList.py ├── printingtask │ ├── Printer.py │ ├── Queue.py │ ├── Simulation.py │ ├── Simulation_inout.txt │ └── Task.py └── rev_string.py ├── chap4-Recursion ├── Programming_Exercises_Chap4.md ├── Stack.py ├── draw_spiral.py ├── factorial_rec.py ├── fibonacci.py ├── hanoi_tower.py ├── intstr_base.py ├── intstrbase_stack.py ├── list_reverse.py ├── listsum_recur.py ├── pascal_triangle.py └── string_reverse.py ├── chap5-SortingSearching ├── HashTable.py ├── HashTableClient.py ├── Numbers.py ├── binary_search.py ├── bubble_sort.py ├── bubble_sort_fast.py ├── hash.py ├── insertion_sort.py ├── merge_sort.py ├── quick_sort.py ├── selection_sort.py └── shell_sort.py └── chap6-BinaryTrees ├── BinaryTree.py └── BinaryTreeClient.py /README.md: -------------------------------------------------------------------------------- 1 | Problem Solving with Algorithms and Data Structures - Python 3 by Brad Miller, David Ranum 2 | ========================================================================================= 3 | 4 | 5 | Motivation: 6 | - Solve problems presented in the book 7 | - Learn python3 8 | - Develop a solution manual for the book 9 | - Have loads of fun along the way :) 10 | -------------------------------------------------------------------------------- /chap1-Introduction/Card.py: -------------------------------------------------------------------------------- 1 | class Card(object): 2 | """ Represents a standard playing card. 3 | Attributes: 4 | suit: Integer 0-3 5 | rank: Integer 1-13 6 | """ 7 | 8 | suit_names = ["Clubs", "Diamonds", "Hearts", "Spades"] 9 | rank_names = [None, "2", "3", "4", "5", "6", "7", "8", 10 | "9", "10", "Jack", "Queen", "King", "Ace"] 11 | 12 | def __init__(self, suit=0, rank=1): 13 | self.suit = suit 14 | self.rank = rank 15 | 16 | def __str__(self): 17 | """ returns a human readable string representation """ 18 | return '%s of %s' % (Card.rank_names[self.rank], 19 | Card.suit_names[self.suit]) 20 | 21 | def __lt__(self, other): 22 | """ Compares this card to other, only by rank (no suit comparison) 23 | Returns a positive number if this > other, negative otherwise. 24 | Returns 0 if the cards are of same rank 25 | """ 26 | # t1 = self.rank 27 | # t2 = other.rank 28 | t1 = self.suit, self.rank 29 | t2 = other.suit, other.rank 30 | return t1 < t2 31 | 32 | def getRank(self): 33 | return self.rank 34 | -------------------------------------------------------------------------------- /chap1-Introduction/Deck.py: -------------------------------------------------------------------------------- 1 | from Card import Card 2 | import random 3 | 4 | 5 | class Deck(object): 6 | """ Represents a deck of cards 7 | Attributes: 8 | cards: list of Card Objects 9 | """ 10 | def __init__(self): 11 | self.cards = [] 12 | for suit in range(4): 13 | for rank in range(1, 14): 14 | card = Card(suit, rank) 15 | self.cards.append(card) 16 | 17 | def __str__(self): 18 | res = [] 19 | for card in self.cards: 20 | res.append(str(card)) 21 | return ", ".join(res) 22 | 23 | def size(self): 24 | return len(self.cards) 25 | 26 | def addCard(self, card): 27 | """ Add a card to the deck""" 28 | self.cards.append(card) 29 | 30 | def addMultipleCards(self, cards): 31 | """ add the multiple cards to existing deck, cards is a list """ 32 | for c in cards: 33 | self.addCard(c) 34 | 35 | def removeCard(self, card): 36 | """ remove a card from the deck""" 37 | self.cards.remove(card) 38 | 39 | def popCard(self, index=0): 40 | """ removes and returns a card from the deck 41 | By default removes the last card from the deck 42 | """ 43 | return self.cards.pop(index) 44 | 45 | def shuffle(self): 46 | """ shuffle the card deck """ 47 | random.shuffle(self.cards) 48 | 49 | def sort(self): 50 | """ sorts the card in ascending order """ 51 | self.cards.sort() 52 | 53 | def moveCards(self, hand, num): 54 | """ Moves the given number of cards from deck to hand 55 | hand: destination hand object 56 | num: integer number of cards to move 57 | """ 58 | for i in range(num): 59 | hand.addCard(self.popCard()) 60 | 61 | 62 | class Hand(Deck): 63 | """ Represents a hand of playing cards """ 64 | def __init__(self, label=''): 65 | self.cards = [] 66 | self.label = label 67 | -------------------------------------------------------------------------------- /chap1-Introduction/Fraction.py: -------------------------------------------------------------------------------- 1 | # Fraction class 2 | from gcd import gcd 3 | 4 | 5 | class Fraction: 6 | 7 | # #2 solution for exercise 2 from 1.7 module chap-1 8 | def __init__(self, top, bottom): 9 | # #5 solution for exercise 5 from 1.7 module - chap-1 10 | if not isinstance(top, int): 11 | valErr = ValueError("{} is not integer".format(top)) 12 | raise valErr 13 | if not isinstance(bottom, int): 14 | valErr = ValueError("{} is not integer".format(bottom)) 15 | raise valErr 16 | # #6 solution for exercise 6 from 1.7 module - chap-1 17 | if top < 0 and bottom < 0: 18 | top = abs(top) 19 | bottom = abs(bottom) 20 | elif bottom < 0: 21 | top = -top 22 | bottom = abs(bottom) 23 | common = gcd(abs(top), abs(bottom)) 24 | self.num = top // common 25 | self.den = bottom // common 26 | 27 | def __str__(self): 28 | return str(self.num) + "/" + str(self.den) 29 | 30 | # #9 solution for exercise 9 from 1.7 module chap-1 31 | def __repr__(self): 32 | return '%s(%r)' % (self.__class__, self.__str__()) 33 | 34 | def show(self): 35 | print (self.num, "/", self.den) 36 | 37 | # #1 solution for programming exercise 1 from 1.7 modude of chap1 38 | def get_num(self): 39 | return self.num 40 | 41 | # #1 solution for programming exercise 1 from 1.7 modude of chap1 42 | def get_den(self): 43 | return self.den 44 | 45 | # #2 solution for exercise 2 from 1.7 module chap-1 46 | def __add__(self, other): 47 | new_num = (self.num * other.den) + (self.den * other.num) 48 | new_den = self.den * other.den 49 | return Fraction(new_num, new_den) 50 | # common = gcd(new_num, new_den) 51 | # return Fraction(new_num // common, new_den // common) 52 | 53 | # #7 solution for exercise 7 from 1.7 module chap-1 54 | def __radd__(self, other): 55 | if other == 0: 56 | return self 57 | else: 58 | other = Fraction(other, 1) 59 | return self.__add__(other) 60 | 61 | # #8 solution for exercise 7 from 1.7 module chap-1 62 | def __iadd__(self, other): 63 | if isinstance(other, int): 64 | other = Fraction(other, 1) 65 | return self.__add__(other) 66 | 67 | def __eq__(self, other): 68 | first_num = self.num * other.den 69 | second_num = self.den * other.num 70 | return first_num == second_num 71 | 72 | # #4 solution for problem 4 for 1.7 module chap-1 73 | def __ne__(self, other): 74 | first_num = self.num * other.den 75 | second_num = self.den * other.num 76 | return first_num != second_num 77 | 78 | # #4 solution for problem 4 for 1.7 module chap-1 79 | def __gt__(self, other): 80 | first_num = self.num * other.den 81 | second_num = self.den * other.num 82 | return first_num > second_num 83 | 84 | # #4 solution for problem 4 for 1.7 module chap-1 85 | def __ge__(self, other): 86 | first_num = self.num * other.den 87 | second_num = self.den * other.num 88 | return first_num >= second_num 89 | 90 | # #4 solution for problem 4 for 1.7 module chap-1 91 | def __lt__(self, other): 92 | first_num = self.num * other.den 93 | second_num = self.den * other.num 94 | return first_num < second_num 95 | 96 | # #4 solution for problem 4 for 1.7 module chap-1 97 | def __le__(self, other): 98 | first_num = self.num * other.den 99 | second_num = self.den * other.num 100 | return first_num <= second_num 101 | 102 | # #3 solution for exercise 3 from 1.7 module chap-1 103 | def __mul__(self, other): 104 | num = self.num * other.num 105 | den = self.den * other.den 106 | return Fraction(num, den) 107 | 108 | # #3 solution for exercise 3 from 1.7 module chap-1 109 | def __truediv__(self, other): 110 | num = self.num * other.den 111 | den = self.den * other.num 112 | common = gcd(abs(num), abs(den)) 113 | return Fraction(num // common, den // common) 114 | 115 | # #3 solution for exercise 3 from 1.7 module chap-1 116 | def __sub__(self, other): 117 | num = (self.num * other.den) - (self.den * other.num) 118 | den = self.den * other.den 119 | common = gcd(abs(num), abs(den)) 120 | return Fraction(num // common, den // common) 121 | -------------------------------------------------------------------------------- /chap1-Introduction/FractionClient.py: -------------------------------------------------------------------------------- 1 | from Fraction import Fraction 2 | from gcd import readInt 3 | 4 | 5 | def readFraction(): 6 | n = readInt('numerator') 7 | d = readInt('denominator') 8 | return Fraction(n, d) 9 | 10 | 11 | def testFractionMultiplication(x, y): 12 | return x * y 13 | 14 | 15 | def testFractionAddition(x, y): 16 | ans = x + y 17 | return ans 18 | 19 | 20 | def testFractionEquality(x, y): 21 | return x == y 22 | 23 | 24 | def testFractionDivision(x, y): 25 | return x / y 26 | 27 | 28 | def testFractionSubtraction(x, y): 29 | return x - y 30 | 31 | 32 | def testFractionGreaterThan(x, y): 33 | return x > y 34 | 35 | 36 | def testFractionLessThan(x, y): 37 | return x < y 38 | 39 | 40 | def testFractionLessThanEqualTo(x, y): 41 | return x <= y 42 | 43 | 44 | def testFractionGreaterThanEqualTo(x, y): 45 | return x >= y 46 | 47 | 48 | def testFractionNotEqualTo(x, y): 49 | return x != y 50 | 51 | 52 | def main(): 53 | print ("Enter first fraction values") 54 | x = readFraction() 55 | print ("Enter second fraction values") 56 | y = readFraction() 57 | ans = testFractionAddition(x, y) 58 | print ("sum of {} and {}: {}".format(x, y, ans)) 59 | ans = testFractionEquality(x, y) 60 | print ("Are {} and {} equal? {}".format(x, y, ans)) 61 | 62 | # #3 testing solution for exercise 3 from 1.7 module chap-1 63 | ans = testFractionMultiplication(x, y) 64 | print ("multiplication of {} and {} is: {}".format(x, y, ans)) 65 | 66 | # #3 testing solution for exercise 3 from 1.7 module chap-1 67 | ans = testFractionDivision(x, y) 68 | print ("Division of {} and {} is: {}".format(x, y, ans)) 69 | 70 | # #3 testing solution for exercise 3 from 1.7 module chap-1 71 | ans = testFractionSubtraction(x, y) 72 | print ("Subtract {} and {} equal to: {}".format(x, y, ans)) 73 | 74 | # #4 testing solution for problem 4 for 1.7 module chap-1 75 | ans = testFractionGreaterThan(x, y) 76 | print ("Is {} greater than {}: {}".format(x, y, ans)) 77 | 78 | # #4 testing solution for problem 4 for 1.7 module chap-1 79 | ans = testFractionLessThan(x, y) 80 | print ("Is {} less than {}: {}".format(x, y, ans)) 81 | 82 | # #4 testing solution for problem 4 for 1.7 module chap-1 83 | ans = testFractionLessThanEqualTo(x, y) 84 | print ("Is {} less than equal to {}: {}".format(x, y, ans)) 85 | 86 | # #4 testing solution for problem 4 for 1.7 module chap-1 87 | ans = testFractionGreaterThanEqualTo(x, y) 88 | print ("Is {} greater than equal to {}: {}".format(x, y, ans)) 89 | 90 | # #4 testing solution for problem 4 for 1.7 module chap-1 91 | ans = testFractionNotEqualTo(x, y) 92 | print ("Is {} not equal to {}: {}".format(x, y, ans)) 93 | 94 | # #1 testing solution for programming exercise 1 from 1.7 module of chap1 95 | print ("Numerator of first fraction: {}".format(x.get_num())) 96 | print ("Denominator of first fraction: {}".format(x.get_den())) 97 | 98 | # #5 testing solution for exercise 5 from 1.7 module - chap-1 99 | # this should raise exception from Fraction class constructor 100 | # Please uncomment below 2 lines to test solution for 5th problem 101 | # f = Fraction(2, 'a') 102 | # print ("Invalid fraction: ", f) 103 | 104 | n = 2 105 | # #7 testing solution for programming exercise 7 from 1.7 module of chap1 106 | ans = n+x 107 | print ("{} plus {} is: {}".format(n, x, ans)) 108 | 109 | old_x = x 110 | # #8 testing solution for programming exercise 8 from 1.7 module of chap1 111 | x += n 112 | print ("{} plus {} is: {}".format(old_x, n, x)) 113 | 114 | x = old_x 115 | # #8 testing solution for programming exercise 8 from 1.7 module of chap1 116 | x += y 117 | print ("{} plus {} is: {}".format(old_x, y, x)) 118 | x = old_x 119 | 120 | # #9 testing solution for programming exercise 9 from 1.7 module of chap1 121 | print ("Testing __repr__: ", repr(x)) 122 | 123 | 124 | if __name__ == '__main__': 125 | main() 126 | -------------------------------------------------------------------------------- /chap1-Introduction/Programming_Exercises.md: -------------------------------------------------------------------------------- 1 | 1.7 Programming Exercises And Solution 2 | ================================================================================= 3 | 4 | #### 1. Implement the simple methods get_num and get_den that will return the numerator and denominator of a fraction. 5 | - Please refer to #1 comment in Fraction.py file (same directory) for solution to this problem. 6 | - Test code is added in the FractionClient.py with #1 comment. 7 | 8 | #### 2. In many ways it would be better if all fractions were maintained in lowest terms right from the start. Modify the constructor for the Fraction class so that GCD is used to reduce fractions immediately. Notice that this means the \_\_add\_\_ function no longer needs to reduce. Make the necessary modifications. 9 | - Refer to #2 comment in the Fraction.py file (current directory) 10 | 11 | #### 3. Implement the remaining simple arithmetic operators ( \_\_sub\_\_ , \_\_mul\_\_ , and \_\_truediv\_\_ ). 12 | - Refer to #3 comment in the Fraction.py and FractionClient.py files. 13 | 14 | #### 4. Implement the remaining relational operators ( \_\_gt\_\_ , \_\_ge\_\_ , \_\_lt\_\_ , \_\_le\_\_ , and \_\_ne\_\_ ) 15 | - Refer to #4 comment in the Fraction.py and FractionClient.py files. 16 | 17 | 18 | #### 5. Modify the constructor for the fraction class so that it checks to make sure that the numerator and denominator are both integers. If either is not an integer the constructor should raise an exception. 19 | - Refer to #5 comment in the Fraction.py and FractionClient.py files. 20 | 21 | 22 | #### 6. In the definition of fractions we assumed that negative fractions have a negative numerator and a positive denominator. Using a negative denominator would cause some of the relational operators to give incorrect results. In general, this is an unnecessary constraint. Modify the constructor to allow the user to pass a negative denominator so that all of the operators continue to work properly. 23 | - Refer to #6 comment in the Fraction.py file. 24 | 25 | 26 | #### 7. Research the \_\_radd\_\_ method. How does it differ from \_\_add\_\_ ? When is it used? Implement \_\_radd\_\_ . 27 | - Refer to #7 comment in the Fraction.py and FractionClient.py File. 28 | 29 | 30 | #### 8. Repeat the last question but this time consider the \_\_iadd\_\_ method. 31 | - Refer to #8 comment in the Fraction.py and FractionClient.py File. 32 | 33 | #### 9. Research the \_\_repr\_\_ method. How does it differ from \_\_str\_\_ ? When is it used? Implement \_\_repr\_\_ . 34 | > **object.\_\_repr\_\_(self):** 35 | > Called by the repr() built-in function and by string conversions (reverse quotes) to compute the “official” string representation of an object. If at all possible, this should look like a valid Python expression that could be used to recreate an object with the same value (given an appropriate environment). If this is not possible, a string of the form <...some useful description...> should be returned. The return value must be a string object. If a class defines \_\_repr\_\_() but not \_\_str\_\_(), then \_\_repr\_\_() is also used when an “informal” string representation of instances of that class is required. 36 | This is typically used for debugging, so it is important that the representation is information-rich and unambiguous. 37 | 38 | > **object.\_\_str\_\_(self):** 39 | > Called by the str() built-in function and by the print statement to compute the “informal” string representation of an object. This differs from __repr__() in that it does not have to be a valid Python expression: a more convenient or concise representation may be used instead. The return value must be a string object. 40 | 41 | - Please refer to #9 comment in Fraction.py and FractionClient.py file. 42 | 43 | #### 10. Design a class to represent a playing card. Now design a class to represent a deck of cards. Using these two classes, implement a favorite card game. 44 | ** We are going to implement the War card game - following resources might be useful for reference** 45 | 46 | > - http://www.pagat.com/war/war.html 47 | > - http://en.wikipedia.org/wiki/War_(card_game) 48 | 49 | **Please refer following files:** 50 | 51 | > * Card.py 52 | > * Deck.py 53 | > * WarCardGame.py 54 | 55 | #### 11. Find a Sudoku puzzle in the local newspaper. Write a program to solve the puzzle. 56 | -------------------------------------------------------------------------------- /chap1-Introduction/SudokuTest.py: -------------------------------------------------------------------------------- 1 | from sudoku import Sudoku 2 | 3 | 4 | def testCross(sudoku): 5 | """ a set of unit tests for testing cross api in Sudoku class """ 6 | assert len(sudoku.getSquares()) == 81 7 | assert len(sudoku.getUnitlist()) == 27 8 | units = sudoku.getUnits() 9 | assert all(len(units[s]) == 3 for s in sudoku.getSquares()) 10 | peers = sudoku.getPeers() 11 | assert all(len(peers[s]) == 20 for s in sudoku.getPeers()) 12 | print ("Units in B2: ") 13 | print (units['B2']) 14 | u1 = ['B1', 'B2', 'B3', 'B4', 'B5', 'B6', 'B7', 'B8', 'B9'] 15 | u2 = ['A2', 'B2', 'C2', 'D2', 'E2', 'F2', 'G2', 'H2', 'I2'] 16 | u3 = ['A1', 'A2', 'A3', 'B1', 'B2', 'B3', 'C1', 'C2', 'C3'] 17 | ub2 = [u2, u1, u3] 18 | print (ub2) 19 | for u in range(3): 20 | for e in range(9): 21 | if units['B2'][u][e] != ub2[u][e]: 22 | print ("E:", e) 23 | print ('ALL B2 Passed!') 24 | assert units['B2'] == ub2 25 | assert peers['C2'] == set(['A2', 'B2', 'D2', 'E2', 'F2', 'G2', 'H2', 'I2', 26 | 'C1', 'C3', 'C4', 'C5', 'C6', 'C7', 'C8', 'C9', 27 | 'A1', 'A3', 'B1', 'B3']) 28 | print ('All TESTS PASS!') 29 | 30 | 31 | def main(): 32 | sd = Sudoku() 33 | testCross(sd) 34 | 35 | if __name__ == '__main__': 36 | main() 37 | -------------------------------------------------------------------------------- /chap1-Introduction/WarCardGame.py: -------------------------------------------------------------------------------- 1 | from Deck import Deck 2 | from Deck import Hand 3 | 4 | 5 | def playwar(): 6 | deck = Deck() 7 | deck.shuffle() 8 | n = deck.size() 9 | playerHand = Hand() 10 | compHand = Hand() 11 | 12 | deck.moveCards(playerHand, (n//2)) 13 | deck.moveCards(compHand, (n//2)) 14 | # playerHand.sort() 15 | # compHand.sort() 16 | print ("Cards in computer hand: ") 17 | print(compHand) 18 | print("Cards in player hands: ") 19 | print(playerHand) 20 | nowPlay(playerHand, compHand) 21 | 22 | 23 | def computerPlay(compDeck, n=1): 24 | key = input("Computer turn, any key for play: to abort game:") 25 | if key == 'n': 26 | return None 27 | cards = [] 28 | if compDeck.size() < n: 29 | return cards 30 | for i in range(n): 31 | cards.append(compDeck.popCard()) 32 | return cards 33 | 34 | 35 | def playerPlay(playerDeck, n=1): 36 | key = input("Player turn, any key for play: to abort game:") 37 | if key == 'n': 38 | return None 39 | cards = [] 40 | if playerDeck.size() < n: 41 | return cards 42 | for i in range(n): 43 | cards.append(playerDeck.popCard()) 44 | return cards 45 | 46 | 47 | def compareCards(computer, player): 48 | """ Assumes computer and player are list of cards 49 | here we compare the topmost card of each computer and player 50 | Return: 51 | 1 - if computer wins the hand 52 | 2 - if player wins the hand 53 | 3 - Tie 54 | 4 - computer wins the game. 55 | 5 - player wins the game. 56 | if one of them has less card left than the other then they loose and 57 | other wins the game. 58 | """ 59 | clen = len(computer) 60 | plen = len(player) 61 | if clen == plen: 62 | if computer[clen-1].getRank() > player[plen-1].getRank(): 63 | return 1 64 | elif computer[clen-1].getRank() < player[plen-1].getRank(): 65 | return 2 66 | else: 67 | return 3 68 | elif clen > plen: 69 | return 4 70 | else: 71 | return 5 72 | 73 | 74 | def showPlayedCards(cards, subject): 75 | print (subject + " played: ") 76 | for card in cards: 77 | print (str(card)) 78 | 79 | 80 | def applyResults(computer, player, tableCards, result): 81 | if result == 1: 82 | print("Computer Won Hand! :)") 83 | computer.addMultipleCards(tableCards) 84 | elif result == 2: 85 | print("You Won Hand! :)") 86 | player.addMultipleCards(tableCards) 87 | elif result == 4: 88 | print ("Computer Won the Game!! Congrats to her :)") 89 | return None 90 | elif result == 5: 91 | print ("YOU WON the Game!! COngratulations :)") 92 | return None 93 | return (computer, player) 94 | 95 | 96 | def checkCompCards(compCards): 97 | if compCards is None: 98 | print ("You terminated the Game!") 99 | return 0 100 | elif len(compCards) == 0: 101 | print ("YOU WON!! COngratulations :)") 102 | return 0 103 | else: 104 | return 1 105 | 106 | 107 | def checkPlayerCards(playerCards): 108 | if playerCards is None: 109 | print ("You terminated the Game!") 110 | return 0 111 | elif len(playerCards) == 0: 112 | print ("Computer Won!! Congrats to her :)") 113 | return 0 114 | else: 115 | return 1 116 | 117 | 118 | def nowPlay(player, computer): 119 | result = 0 120 | while True: 121 | if result == 3: 122 | n = 2 123 | print ("EQUAL Cards in Rank - Now each player will put 2 cards\n" + 124 | "on table. One face down and one face up. Player having\n" + 125 | "high face up card would win the hand and take all the\n" 126 | + "cards on the table.") 127 | else: 128 | n = 1 129 | tableCards = [] 130 | compCards = computerPlay(computer, n) 131 | check = checkCompCards(compCards) 132 | if check == 0: 133 | return 134 | tableCards.extend(compCards) 135 | showPlayedCards(compCards, "Computer") 136 | 137 | playerCards = playerPlay(player, n) 138 | check = checkPlayerCards(playerCards) 139 | if check == 0: 140 | return 141 | 142 | tableCards.extend(playerCards) 143 | showPlayedCards(playerCards, "You") 144 | 145 | result = compareCards(compCards, playerCards) 146 | if result != 3: 147 | result = applyResults(computer, player, tableCards, result) 148 | if result is not None: 149 | computer, player = result 150 | result = 0 151 | else: 152 | return 153 | 154 | 155 | def main(): 156 | playwar() 157 | 158 | 159 | if __name__ == '__main__': 160 | main() 161 | -------------------------------------------------------------------------------- /chap1-Introduction/gcd.py: -------------------------------------------------------------------------------- 1 | 2 | def readInt(val=''): 3 | while True: 4 | n = input("value {}: ".format(val)) 5 | try: 6 | n = int(n) 7 | return n 8 | except ValueError: 9 | print ("Non numeric input - please try again!") 10 | 11 | 12 | def gcd(n, m): 13 | if n == 0 or m == 0: 14 | return None 15 | if n < m: 16 | n, m == m, n 17 | 18 | while n % m != 0: 19 | old_n = n 20 | old_m = m 21 | n = old_m 22 | m = old_n % old_m 23 | return m 24 | 25 | 26 | def main(): 27 | n = readInt('n') 28 | m = readInt('m') 29 | ans = gcd(n, m) 30 | print ("GCD of {} and {} is: {}".format(n, m, ans)) 31 | 32 | 33 | if __name__ == '__main__': 34 | main() 35 | -------------------------------------------------------------------------------- /chap1-Introduction/sudoku.py: -------------------------------------------------------------------------------- 1 | class Sudoku(object): 2 | 3 | def __init__(self): 4 | self.digits = '123456789' 5 | self.rows = 'ABCDEFGHI' 6 | self.cols = self.digits 7 | self.squares = self.cross(self.rows, self.cols) 8 | self.unitlist = ([self.cross(self.rows, c) for c in self.cols] + 9 | [self.cross(r, self.cols) for r in self.rows] + 10 | [self.cross(rs, cs) for rs in ('ABC', 'DEF', 'GHI') 11 | for cs in ('123', '456', '789')]) 12 | self.units = dict((s, [u for u in self.unitlist if s in u]) 13 | for s in self.squares) 14 | self.peers = dict((s, set(sum(self.units[s], [])) - set([s])) 15 | for s in self.squares) 16 | 17 | def cross(self, A, B): 18 | """ Cross product of elements in A and B """ 19 | return [a+b for a in A for b in B] 20 | 21 | def getSquares(self): 22 | return self.squares 23 | 24 | def getUnitlist(self): 25 | return self.unitlist 26 | 27 | def getUnits(self): 28 | return self.units 29 | 30 | def getPeers(self): 31 | return self.peers 32 | -------------------------------------------------------------------------------- /chap2-AlgoAnalysis/Anagram.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | def checkAnagram(s1, s2): 4 | if s1 is None or s2 is None: 5 | print ("One of the input string is Null") 6 | return False 7 | 8 | s1len = len(s1) 9 | s2len = len(s2) 10 | 11 | if s1len != s2len: 12 | return False 13 | 14 | s1_list = list(s1.lower()) 15 | s2_list = list(s2.lower()) 16 | 17 | s1_list.sort() 18 | s2_list.sort() 19 | 20 | for i in range(s1len): 21 | if s1_list[i] != s2_list[i]: 22 | return False 23 | return True 24 | 25 | 26 | def readInput(): 27 | first = input("Enter first string: ") 28 | second = input("Enter second string: ") 29 | return (first.strip(), second.strip()) 30 | 31 | 32 | def main(): 33 | s1, s2 = readInput() 34 | result = checkAnagram(s1, s2) 35 | print ("are \"{}\" and \"{}\" anagrams? -> {}".format(s1, s2, result)) 36 | 37 | 38 | if __name__ == '__main__': 39 | main() 40 | -------------------------------------------------------------------------------- /chap2-AlgoAnalysis/Anagram_N.py: -------------------------------------------------------------------------------- 1 | def checkAnagram(s1, s2): 2 | if s1 is None or s2 is None: 3 | return False 4 | 5 | s1len = len(s1) 6 | s2len = len(s2) 7 | 8 | if s1len != s2len: 9 | return False 10 | 11 | s1_list = list(s1.lower()) 12 | s2_list = list(s2.lower()) 13 | 14 | s1_map = {} 15 | s2_map = {} 16 | 17 | # Get the unique chars from s1 18 | char_set = set([]) 19 | 20 | # create a map of char -> count for s1 21 | for i in s1_list: 22 | count = 0 23 | if i in s1_map: 24 | count = s1_map[i] 25 | s1_map[i] = (count + 1) 26 | char_set.add(i) 27 | 28 | # create a map of char -> count for s2 29 | for j in s2_list: 30 | count = 0 31 | if j in s2_map: 32 | count = s2_map[j] 33 | s2_map[j] = (count + 1) 34 | 35 | # check if chars in s1 have the same count as in s2 string. 36 | for c in char_set: 37 | if c not in s2_map: 38 | return False 39 | if s1_map[c] != s2_map[c]: 40 | return False 41 | # Every condition pertaining to anagrams holds true - so lets return True :) 42 | return True 43 | 44 | 45 | def readInput(): 46 | s1 = input("Enter first string: ") 47 | s2 = input("Enter second string: ") 48 | return (s1.strip(), s2.strip()) 49 | 50 | 51 | def main(): 52 | s1, s2 = readInput() 53 | ans = checkAnagram(s1, s2) 54 | print ("Are \"{}\" and \"{}\" anagrams? -> {}".format(s1, s2, ans)) 55 | 56 | 57 | if __name__ == '__main__': 58 | main() 59 | -------------------------------------------------------------------------------- /chap2-AlgoAnalysis/Anagram_N_inout.txt: -------------------------------------------------------------------------------- 1 | Enter first string: heart 2 | Enter second string: Earth 3 | Are "heart" and "Earth" anagrams? -> True 4 | ================================================= 5 | Enter first string: apple 6 | Enter second string: Pleap 7 | Are "apple" and "Pleap" anagrams? -> True 8 | ================================================= 9 | Enter first string: dimple 10 | Enter second string: pimple 11 | Are "dimple" and "pimple" anagrams? -> False 12 | ================================================= 13 | Enter first string: 14 | Enter second string: time 15 | Are "" and "time" anagrams? -> False 16 | -------------------------------------------------------------------------------- /chap2-AlgoAnalysis/Anagram_inout.txt: -------------------------------------------------------------------------------- 1 | Enter first string: earth 2 | Enter second string: Heart 3 | are "earth" and "Heart" anagrams? -> True 4 | ========================================================= 5 | Enter first string: python 6 | Enter second string: typhon 7 | are "python" and "typhon" anagrams? -> True 8 | ======================================================== 9 | Enter first string: leafs 10 | Enter second string: feale 11 | are "leafs" and "feale" anagrams? -> False 12 | 13 | -------------------------------------------------------------------------------- /chap2-AlgoAnalysis/FindKMin.py: -------------------------------------------------------------------------------- 1 | import random 2 | 3 | 4 | def partition(L, p, r): 5 | x = L[r] 6 | i = p-1 7 | for j in range(p, r): 8 | if L[j] <= x: 9 | i += 1 10 | L[i], L[j] = L[j], L[i] 11 | L[i+1], L[r] = L[r], L[i+1] 12 | return i+1 13 | 14 | 15 | def randomPartition(L, p, r): 16 | if p == r: 17 | return p 18 | i = random.randint(p, r) 19 | L[i], L[r] = L[r], L[i] 20 | return partition(L, p, r) 21 | 22 | 23 | def randomizedSelect(L, p, r, i): 24 | if p == r: 25 | return L[p] 26 | q = randomPartition(L, p, r) 27 | k = q - p + 1 28 | if i == k: 29 | return L[q] 30 | elif i < k: 31 | return randomizedSelect(L, p, q-1, i) 32 | else: 33 | return randomizedSelect(L, q+1, r, i-k) 34 | 35 | 36 | def kthMinLinear(L, k): 37 | """ Assumption: L is a list of numbers. k is the index to be found. 38 | time complexity: O(n) - expected linear time implementation 39 | randomizedSelect method finds the Kth elemenet by repeated partitioning 40 | the List around randomly chosen pivot. 41 | """ 42 | if L is None: 43 | return None 44 | if k < 0 or k > len(L): 45 | return None 46 | value = randomizedSelect(L, 0, len(L)-1, k) 47 | return value 48 | 49 | 50 | def kthMin(L, k): 51 | """ Assumption is L is a list, here we will sort the list and return the 52 | Kth index - time complexity: O(nlog(n)) """ 53 | if L is None: 54 | return None 55 | if k < 0 or k > len(L): 56 | return None 57 | L.sort() 58 | return L[k-1] 59 | 60 | 61 | def readInput(): 62 | flag = True 63 | while flag: 64 | try: 65 | print ("Enter comma separated numbers: ") 66 | S = input() 67 | S = S.split(",") 68 | S = [int(s.strip()) for s in S] 69 | flag = False 70 | except ValueError as e: 71 | print ("Invalid entry! Try Again..", e) 72 | 73 | flag = True 74 | while flag: 75 | try: 76 | print ("Enter k value: ") 77 | k = input() 78 | k = int(k.strip()) 79 | flag = False 80 | except ValueError as e: 81 | print ("Bad Input! try again..", e) 82 | 83 | return (S, k) 84 | 85 | 86 | def main(): 87 | L, k = readInput() 88 | print ("Sorted list for reference is: ") 89 | print (sorted(L)) 90 | kthVal = kthMin(L, k) 91 | print ("{} min value in list is - log_linear: {}".format(k, kthVal)) 92 | random.shuffle(L) 93 | kthVal = kthMinLinear(L, k) 94 | print ("{} min value in list is - expected_linear: {}".format(k, kthVal)) 95 | 96 | 97 | if __name__ == '__main__': 98 | main() 99 | -------------------------------------------------------------------------------- /chap2-AlgoAnalysis/FindKMin_inout.txt: -------------------------------------------------------------------------------- 1 | Enter comma separated numbers: 2 | 8,6,4,2,12,5,9 3 | Enter k value: 4 | 5 5 | Sorted list for reference is: 6 | [2, 4, 5, 6, 8, 9, 12] 7 | 5 min value in list is - log_linear: 8 8 | 5 min value in list is - expected_linear: 8 9 | 10 | -------------------------------------------------------------------------------- /chap2-AlgoAnalysis/FindMin.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | def findMinN2(nums): 4 | if nums is None or len(nums) < 1: 5 | return None 6 | for i in range(0, len(nums)-1): 7 | for j in range(i+1, len(nums)): 8 | if nums[i] > nums[j]: 9 | # tmp = nums[i] 10 | # nums[i] = nums[j] 11 | # nums[j] = tmp 12 | nums[i], nums[j] = nums[j], nums[i] 13 | return nums[0] 14 | 15 | 16 | def findMinFast(nums): 17 | if nums is None or len(nums) < 1: 18 | return None 19 | minN = nums[0] 20 | for i in range(1, len(nums)): 21 | if nums[i] < minN: 22 | minN = nums[i] 23 | return minN 24 | 25 | 26 | def readInput(): 27 | print ("Enter list of numbers - comma separated: ") 28 | inp = input() 29 | st = inp.split(",") 30 | return [int(s.strip()) for s in st] 31 | 32 | 33 | def main(): 34 | inp = readInput() 35 | minN = findMinN2(inp) 36 | print ("Minimum(O(n^2)) is: {}".format(minN)) 37 | minN = findMinFast(inp) 38 | print ("Minimum(O(n)) is: {}".format(minN)) 39 | 40 | 41 | if __name__ == '__main__': 42 | main() 43 | -------------------------------------------------------------------------------- /chap2-AlgoAnalysis/FindMin_inout.txt: -------------------------------------------------------------------------------- 1 | Enter list of numbers - comma separated: 2 | 9, 8, 7, 19, 11, 12, 16, 15, 25, 2, 34, 36, 31, 61, 29 3 | Minimum(O(n^2)) is: 2 4 | Minimum(O(n)) is: 2 5 | 6 | -------------------------------------------------------------------------------- /chap2-AlgoAnalysis/Programming_Exercises_2.md: -------------------------------------------------------------------------------- 1 | ## 2.7 Chapter-2 Programming Exercises - Algorithm Analysis 2 | ===================================================================== 3 | 4 | ##### 1. Devise an experiment to verify that the list index operator is O(1). 5 | > Please refer to the file list_index.py 6 | > **Approach** : we create a list of size 1000000 and do 10K index access, 7 | > then we increase the size of list 1M at each step and continue with 10K index access. 8 | > As we can see in the list_index_input.txt the time remains constant (0.038751423 secs) 9 | > So we can conclude the access time does not depend on list length and is constant ie O(1) 10 | 11 | ##### 2. Devise an experiment to verify that get item and set item are O(1) for dictionaries. 12 | > Refer to file dict_getset.py for implementation and dict_getset_input.txt for output. 13 | > **Approach** : We do n number of sets and gets in dictionary and measure the time for 14 | > 1 set and get and see that its fairly constant and independent of the size of the dictionary. 15 | 16 | ##### 3. Devise an experiment that compares the performance of the del operator on lists and dictionaries. 17 | > Refer to file del_dictlist.py for implementation and del_dictlist_inout.txt for output. 18 | > **Approach**: First we insert 100K to 1M (step of 100K) elements in dictionary and lists 19 | > and then call 100K to 1M del operations(step of 100K) on each of them. As seen in the 20 | > del_dictlist_inout.txt file - dict del operations are much much faster(100X+) than del on lists. 21 | 22 | ##### 4. Given a list of numbers in random order write a linear time algorithm to find the kth smallest number in the list. 23 | Explain why your algorithm is linear. 24 | > Please refer FindKMin.py file for implementation. 25 | > Sample input/output is captured in FindKMin_inout.txt 26 | 27 | ##### 5. Can you improve the algorithm from the previous problem to be O(n log(n))? 28 | > Please refer FindKMin.py file for implementation. 29 | > Sample input/output is captured in FindKMin_inout.txt 30 | -------------------------------------------------------------------------------- /chap2-AlgoAnalysis/SumOfN.py: -------------------------------------------------------------------------------- 1 | import time 2 | 3 | 4 | def sumOfN(n): 5 | start = time.time() 6 | 7 | sumN = 0 8 | for i in range(1, n+1): 9 | sumN += i 10 | end = time.time() 11 | 12 | return (sumN, end-start) 13 | 14 | 15 | def sumOfNFast(n): 16 | start = time.time() 17 | nSum = (n*(n+1))/2 18 | end = time.time() 19 | 20 | return (nSum, end-start) 21 | 22 | 23 | def main(): 24 | # n = int(input("Enter integer n: ")) 25 | 26 | testNums = [10000, 100000, 1000000, 10000000] 27 | 28 | for n in testNums: 29 | nSum, ttime = sumOfN(n) 30 | print ("Summation of %d numbers is %d taking %10.7f seconds" 31 | % (n, nSum, ttime)) 32 | nSum, ttime = sumOfNFast(n) 33 | print ("Fast Summation of %d numbers is %d taking %10.7f seconds" 34 | % (n, nSum, ttime)) 35 | 36 | 37 | if __name__ == '__main__': 38 | main() 39 | -------------------------------------------------------------------------------- /chap2-AlgoAnalysis/SumOfN_Run.txt: -------------------------------------------------------------------------------- 1 | Summation of 10000 numbers is 50005000 taking 0.0015683 seconds 2 | Fast Summation of 10000 numbers is 50005000 taking 0.0000017 seconds 3 | Summation of 100000 numbers is 5000050000 taking 0.0171022 seconds 4 | Fast Summation of 100000 numbers is 5000050000 taking 0.0000019 seconds 5 | Summation of 1000000 numbers is 500000500000 taking 0.1789942 seconds 6 | Fast Summation of 1000000 numbers is 500000500000 taking 0.0000026 seconds 7 | Summation of 10000000 numbers is 50000005000000 taking 1.7879784 seconds 8 | Fast Summation of 10000000 numbers is 50000005000000 taking 0.0000031 seconds 9 | -------------------------------------------------------------------------------- /chap2-AlgoAnalysis/del_dictlist.py: -------------------------------------------------------------------------------- 1 | import timeit 2 | 3 | 4 | # D = {i: i for i in range(100000)} 5 | D = {} 6 | # L = [j for j in range(100000)] 7 | L = [] 8 | n = 100000 9 | 10 | 11 | def main(): 12 | for n in range(100000, 1000001, 100000): 13 | print ("Executing %d deletes on dict" % (n)) 14 | dictDel = timeit.Timer(""" 15 | D = {i: i for i in range(n)} 16 | for i in range(n): 17 | del D[i] 18 | """, 19 | "from __main__ import D, n") 20 | ddit = dictDel.timeit(1) 21 | print ("Dict del time per operation: %12.12f secs" % (ddit/float(n))) 22 | print ("\n") 23 | for n in range(100000, 1000001, 100000): 24 | print ("Executing %d deletes on list" % (n)) 25 | listDel = timeit.Timer(""" 26 | L = [i for i in range(n)] 27 | for i in range(n-1): 28 | del L[0] 29 | """, 30 | "from __main__ import L, n") 31 | ldit = listDel.timeit(1) 32 | print ("List Del Time per operation: %12.12f secs" % (ldit/float(n))) 33 | 34 | 35 | if __name__ == '__main__': 36 | main() 37 | -------------------------------------------------------------------------------- /chap2-AlgoAnalysis/del_dictlist_inout.txt: -------------------------------------------------------------------------------- 1 | Executing 100000 deletes on dict 2 | Dict del time per operation: 0.000000441241 secs 3 | Executing 200000 deletes on dict 4 | Dict del time per operation: 0.000000172615 secs 5 | Executing 300000 deletes on dict 6 | Dict del time per operation: 0.000000099240 secs 7 | Executing 400000 deletes on dict 8 | Dict del time per operation: 0.000000083250 secs 9 | Executing 500000 deletes on dict 10 | Dict del time per operation: 0.000000057852 secs 11 | Executing 600000 deletes on dict 12 | Dict del time per operation: 0.000000048242 secs 13 | Executing 700000 deletes on dict 14 | Dict del time per operation: 0.000000057865 secs 15 | Executing 800000 deletes on dict 16 | Dict del time per operation: 0.000000037080 secs 17 | Executing 900000 deletes on dict 18 | Dict del time per operation: 0.000000032459 secs 19 | Executing 1000000 deletes on dict 20 | Dict del time per operation: 0.000000037013 secs 21 | 22 | 23 | Executing 100000 deletes on list 24 | List Del Time per operation: 0.000035710628 secs 25 | Executing 200000 deletes on list 26 | List Del Time per operation: 0.000017075064 secs 27 | Executing 300000 deletes on list 28 | List Del Time per operation: 0.000011254690 secs 29 | Executing 400000 deletes on list 30 | List Del Time per operation: 0.000008568728 secs 31 | Executing 500000 deletes on list 32 | List Del Time per operation: 0.000006982214 secs 33 | Executing 600000 deletes on list 34 | List Del Time per operation: 0.000005812062 secs 35 | Executing 700000 deletes on list 36 | List Del Time per operation: 0.000005089027 secs 37 | Executing 800000 deletes on list 38 | List Del Time per operation: 0.000004390891 secs 39 | Executing 900000 deletes on list 40 | List Del Time per operation: 0.000003936278 secs 41 | Executing 1000000 deletes on list 42 | List Del Time per operation: 0.000003436626 secs 43 | -------------------------------------------------------------------------------- /chap2-AlgoAnalysis/dict_getset.py: -------------------------------------------------------------------------------- 1 | import timeit 2 | 3 | D = {} 4 | 5 | 6 | def timeDictSetFn(n): 7 | global D 8 | for i in range(n): 9 | D[i] = i 10 | 11 | 12 | def timeDictGetFn(n): 13 | global D 14 | for i in range(n): 15 | x = D[i] 16 | 17 | 18 | def main(): 19 | 20 | k = 10 21 | print ("Lets check the dict set times: ") 22 | for n in range(100000, 1000001, 100000): 23 | setTime = timeit.Timer("timeDictSetFn("+str(n)+")", 24 | "from __main__ import timeDictSetFn") 25 | sit = setTime.timeit(k) 26 | print ("Time for {} dict inserts: {}".format(n, sit)) 27 | print ("Time for {} dict inserts: {}".format(1, sit/float(k*n))) 28 | 29 | print ("Now lets check the dict get times: ") 30 | for n in range(100000, 1000001, 100000): 31 | setTime = timeit.Timer("timeDictSetFn("+str(n)+")", 32 | "from __main__ import timeDictSetFn") 33 | getTime = timeit.Timer("timeDictGetFn("+str(n)+")", 34 | "from __main__ import timeDictGetFn") 35 | git = getTime.timeit(k) 36 | print ("Time for {} dict gets: {}".format(n, git)) 37 | print ("Time for {} dict gets: {}".format(1, git/float(k*n))) 38 | 39 | if __name__ == '__main__': 40 | main() 41 | -------------------------------------------------------------------------------- /chap2-AlgoAnalysis/dict_getset_input.txt: -------------------------------------------------------------------------------- 1 | Lets check the dict set times: 2 | Time for 100000 dict inserts: 0.2022065460041631 3 | Time for 1 dict inserts: 2.0220654600416311e-07 4 | Time for 200000 dict inserts: 0.42527797099319287 5 | Time for 1 dict inserts: 2.1263898549659644e-07 6 | Time for 300000 dict inserts: 0.5718423299840651 7 | Time for 1 dict inserts: 1.9061410999468838e-07 8 | Time for 400000 dict inserts: 0.798263285018038 9 | Time for 1 dict inserts: 1.995658212545095e-07 10 | Time for 500000 dict inserts: 0.9656122370215598 11 | Time for 1 dict inserts: 1.9312244740431197e-07 12 | Time for 600000 dict inserts: 1.181954028987093 13 | Time for 1 dict inserts: 1.9699233816451548e-07 14 | Time for 700000 dict inserts: 1.4146682010032237 15 | Time for 1 dict inserts: 2.0209545728617482e-07 16 | Time for 800000 dict inserts: 1.5430915000033565 17 | Time for 1 dict inserts: 1.9288643750041957e-07 18 | Time for 900000 dict inserts: 1.7338465139910113 19 | Time for 1 dict inserts: 1.926496126656679e-07 20 | Time for 1000000 dict inserts: 1.9445611519913655 21 | Time for 1 dict inserts: 1.9445611519913654e-07 22 | Now lets check the dict get times: 23 | Time for 100000 dict gets: 0.17271794399130158 24 | Time for 1 dict gets: 1.727179439913016e-07 25 | Time for 200000 dict gets: 0.3504196810245048 26 | Time for 1 dict gets: 1.752098405122524e-07 27 | Time for 300000 dict gets: 0.5403476940118708 28 | Time for 1 dict gets: 1.801158980039569e-07 29 | Time for 400000 dict gets: 0.7135444059967995 30 | Time for 1 dict gets: 1.7838610149919986e-07 31 | Time for 500000 dict gets: 0.8621826440212317 32 | Time for 1 dict gets: 1.7243652880424633e-07 33 | Time for 600000 dict gets: 1.0694317989982665 34 | Time for 1 dict gets: 1.7823863316637774e-07 35 | Time for 700000 dict gets: 1.2612103990104515 36 | Time for 1 dict gets: 1.801729141443502e-07 37 | Time for 800000 dict gets: 1.4259053679998033 38 | Time for 1 dict gets: 1.782381709999754e-07 39 | Time for 900000 dict gets: 1.688889402023051 40 | Time for 1 dict gets: 1.876543780025612e-07 41 | Time for 1000000 dict gets: 1.9324668380140793 42 | Time for 1 dict gets: 1.932466838014079e-07 43 | -------------------------------------------------------------------------------- /chap2-AlgoAnalysis/list_index.py: -------------------------------------------------------------------------------- 1 | import random 2 | import timeit 3 | 4 | testList = list(range(10000)) 5 | k = 10000 6 | def verifyListIndexOrderOne(testList, n): 7 | # testList = list(range(n)) 8 | # n = len(testList) 9 | for i in range(k): 10 | index = random.randint(0, k-1) 11 | testList[index] 12 | 13 | 14 | def main(): 15 | # verifyListIndexOrderOne(1000000) 16 | for n in range(1000000, 10000001, 1000000): 17 | testList = list(range(n)) 18 | indexTime = timeit.Timer("verifyListIndexOrderOne(testList,"+str(n)+")", 19 | "from __main__ import testList,\ 20 | verifyListIndexOrderOne") 21 | it = indexTime.timeit(number=1) 22 | print ("TOTAL TIME for %d index access in %d list of"\ 23 | "numbers :%15.9f seconds" % (k, n, it)) 24 | 25 | 26 | if __name__ == '__main__': 27 | main() 28 | -------------------------------------------------------------------------------- /chap2-AlgoAnalysis/list_index_input.txt: -------------------------------------------------------------------------------- 1 | TOTAL TIME for 10000 index access in 1000000 list ofnumbers : 0.038751423 seconds 2 | TOTAL TIME for 10000 index access in 2000000 list ofnumbers : 0.039394982 seconds 3 | TOTAL TIME for 10000 index access in 3000000 list ofnumbers : 0.040124800 seconds 4 | TOTAL TIME for 10000 index access in 4000000 list ofnumbers : 0.039911843 seconds 5 | TOTAL TIME for 10000 index access in 5000000 list ofnumbers : 0.038808707 seconds 6 | TOTAL TIME for 10000 index access in 6000000 list ofnumbers : 0.038899759 seconds 7 | TOTAL TIME for 10000 index access in 7000000 list ofnumbers : 0.038874536 seconds 8 | TOTAL TIME for 10000 index access in 8000000 list ofnumbers : 0.038580567 seconds 9 | TOTAL TIME for 10000 index access in 9000000 list ofnumbers : 0.038633072 seconds 10 | TOTAL TIME for 10000 index access in 10000000 list ofnumbers : 0.038984403 seconds 11 | -------------------------------------------------------------------------------- /chap2-AlgoAnalysis/timer.py: -------------------------------------------------------------------------------- 1 | import timeit 2 | 3 | 4 | def testList(): 5 | pop_zero = timeit.Timer("popZero()", "from __main__ import popZero") 6 | pop_end = timeit.Timer("popEnd()", "from __main__ import popEnd") 7 | 8 | print("pop(0) pop()") 9 | pt = pop_end.timeit(number=1000) 10 | pz = pop_zero.timeit(number=1000) 11 | print("POP ZERO: %15.5f" % (pz)) 12 | print("POP END: %15.5f" % (pt)) 13 | 14 | 15 | def popZero(): 16 | i = 10000 17 | x = list(range(i)) 18 | # while len(x) > 0: 19 | for j in range(i): 20 | x.pop(0) 21 | 22 | 23 | def popEnd(): 24 | i = 10000 25 | x = list(range(i)) 26 | # while len(x) > 0: 27 | for j in range(i): 28 | x.pop() 29 | 30 | 31 | def main(): 32 | testList() 33 | 34 | 35 | if __name__ == '__main__': 36 | main() 37 | -------------------------------------------------------------------------------- /chap2-AlgoAnalysis/timer_inout.txt: -------------------------------------------------------------------------------- 1 | pop(0) pop() 2 | POP ZERO: 0.81363 3 | POP END: 0.26686 4 | ================================================================= 5 | pop(0) pop() 6 | POP ZERO: 0.60401 7 | POP END: 0.28012 8 | ================================================================= 9 | pop(0) pop() 10 | POP ZERO: 35.26890 11 | POP END: 3.02529 12 | 13 | -------------------------------------------------------------------------------- /chap3-BasicDataStructure/BalancedSymbols.py: -------------------------------------------------------------------------------- 1 | from Stack import Stack 2 | 3 | 4 | def checkBalancedSymbol(string): 5 | """ complete parenthesis checker for [,{,(,),},] 6 | <><>{}([]), (({})), ()[]<>[] are all valid strings. 7 | <>(({}), {}<>> are not """ 8 | S = Stack() 9 | index = 0 10 | balanced = True 11 | slen = len(string) 12 | while index < slen: 13 | if string[index] in "{[(<": 14 | S.push(string[index]) 15 | else: 16 | if S.is_empty() or (not checkMatch(S.peek(), string[index])): 17 | balanced = False 18 | break 19 | else: 20 | S.pop() 21 | index += 1 22 | if not S.is_empty(): 23 | balanced = False 24 | return balanced 25 | 26 | 27 | def checkMatch(open, close): 28 | if close == ')' and open == '(': 29 | return True 30 | elif close == '}' and open == '{': 31 | return True 32 | elif close == ']' and open == '[': 33 | return True 34 | elif close == '>' and open == '<': 35 | return True 36 | return False 37 | 38 | 39 | def readInput(): 40 | while True: 41 | found = True 42 | symStr = input("Enter String: ") 43 | for s in symStr: 44 | if s not in "{([<>])}": 45 | found = False 46 | print ("Invalid String. Please try again...") 47 | break 48 | if found: 49 | break 50 | return symStr 51 | 52 | 53 | def main(): 54 | symStr = readInput() 55 | print ("Symbol String: ", symStr) 56 | print ("Are symbols in string balanced: ", checkBalancedSymbol(symStr)) 57 | 58 | 59 | if __name__ == '__main__': 60 | main() 61 | -------------------------------------------------------------------------------- /chap3-BasicDataStructure/BaseConverter.py: -------------------------------------------------------------------------------- 1 | from Stack import Stack 2 | 3 | 4 | def baseConverter(decNum, base): 5 | """ Converts decimal number greater than ZERO to any base 6 | less than 16 and greater than 1""" 7 | digits = "0123456789ABCDEF" 8 | S = Stack() 9 | while decNum > 0: 10 | mod = decNum % base 11 | S.push(digits[mod]) 12 | decNum = decNum // base 13 | 14 | L = [] 15 | while not S.is_empty(): 16 | L.append(S.pop()) 17 | 18 | return ''.join(L) 19 | 20 | 21 | def readInput(): 22 | while True: 23 | num = input("Enter decimal number: ") 24 | try: 25 | decNum = int(num) 26 | if decNum < 0: 27 | print ("Enter number greater than ZERO.") 28 | continue 29 | break 30 | except ValueError: 31 | print ("Bad Input, Please try again..") 32 | while True: 33 | base = input("Enter base for decimal to convert: ") 34 | try: 35 | base = int(base) 36 | if base < 2 or base > 16: 37 | print ("Invalid base. Please enter between 2 & 16") 38 | continue 39 | break 40 | except ValueError: 41 | print ("Bad Input, Try again ...") 42 | return (decNum, base) 43 | 44 | 45 | def main(): 46 | decNum, base = readInput() 47 | result = baseConverter(decNum, base) 48 | print ("{} in base {} is: {}".format(decNum, base, result)) 49 | 50 | 51 | if __name__ == '__main__': 52 | main() 53 | -------------------------------------------------------------------------------- /chap3-BasicDataStructure/Calculator.py: -------------------------------------------------------------------------------- 1 | from Tokenizer import tokenizer 2 | from Tokenizer import readInput 3 | from ParenChecker import parenChecker 4 | from InfixToPostfix import infixToPostfix 5 | from PostfixEval import postfixEvaluator 6 | 7 | 8 | def calculator(): 9 | while True: 10 | expr = readInput() 11 | tokens = tokenizer(expr) 12 | parens = [] 13 | for t in tokens: 14 | if t == '(' or t == ')': 15 | parens.append(t) 16 | if not parenChecker(''.join(parens)): 17 | print ("Parenthesis mismatch. Please try again..") 18 | continue 19 | 20 | if (tokens is not None) or (len(tokens) >= 1): 21 | postfix = infixToPostfix(tokens) 22 | print ("postfix: ", postfix) 23 | result = postfixEvaluator(postfix) 24 | print ("ANS: ", result) 25 | print ("press any key to continue, \'n\' to exit.") 26 | ch = input() 27 | if ch == 'n': 28 | break 29 | 30 | 31 | def main(): 32 | calculator() 33 | 34 | 35 | if __name__ == '__main__': 36 | main() 37 | -------------------------------------------------------------------------------- /chap3-BasicDataStructure/Calculator_inout.txt: -------------------------------------------------------------------------------- 1 | Enter expression for calculator: 2 | 2+3 3 | token list: ['2', '+', '3'] 4 | postfix: 2 3 + 5 | ANS: 5 6 | press any key to continue, 'n' to exit. 7 | a 8 | Enter expression for calculator: 9 | 6+7*2 10 | token list: ['6', '+', '7', '*', '2'] 11 | postfix: 6 7 2 * + 12 | ANS: 20 13 | press any key to continue, 'n' to exit. 14 | a 15 | Enter expression for calculator: 16 | (1+4)^(2+3) 17 | token list: ['(', '1', '+', '4', ')', '^', '(', '2', '+', '3', ')'] 18 | postfix: 1 4 + 2 3 + ^ 19 | ANS: 3125 20 | press any key to continue, 'n' to exit. 21 | a 22 | Enter expression for calculator: 23 | ((2+3)*(1+4)) 24 | token list: ['(', '(', '2', '+', '3', ')', '*', '(', '1', '+', '4', ')', ')'] 25 | postfix: 2 3 + 1 4 + * 26 | ANS: 25 27 | press any key to continue, 'n' to exit. 28 | 29 | Enter expression for calculator: 30 | 31 | token list: [] 32 | postfix: 33 | Invalid postfix expression 34 | ANS: None 35 | press any key to continue, 'n' to exit. 36 | -------------------------------------------------------------------------------- /chap3-BasicDataStructure/DecToBin.py: -------------------------------------------------------------------------------- 1 | from Stack import Stack 2 | 3 | 4 | def decToBin(num): 5 | """ Given decimal number D > 0 as input - convert it to binary string """ 6 | S = Stack() 7 | while num > 1: 8 | m = num % 2 9 | S.push(m) 10 | num = num//2 11 | S.push(num) 12 | L = [] 13 | while not S.is_empty(): 14 | L.append(S.pop()) 15 | return ''.join([str(i) for i in L]) 16 | 17 | 18 | def readInput(): 19 | while True: 20 | num = input("Enter decimal number: ") 21 | try: 22 | num = int(num) 23 | if (num <= 0): 24 | print ("Please enter postive number!") 25 | continue 26 | return num 27 | except ValueError: 28 | print ("Bad Input! Please try again..") 29 | 30 | 31 | def main(): 32 | decNum = readInput() 33 | binNum = decToBin(decNum) 34 | print ("{} in binary is: {}".format(decNum, binNum)) 35 | 36 | if __name__ == '__main__': 37 | main() 38 | -------------------------------------------------------------------------------- /chap3-BasicDataStructure/Deque.py: -------------------------------------------------------------------------------- 1 | class Deque: 2 | 3 | def __init__(self): 4 | self.items = [] 5 | 6 | def isEmpty(self): 7 | return self.items == [] 8 | 9 | def addFront(self, data): 10 | self.items.append(data) 11 | 12 | def addRear(self, data): 13 | self.items.insert(0, data) 14 | 15 | def removeFront(self): 16 | return self.items.pop() 17 | 18 | def removeRear(self): 19 | return self.items.pop(0) 20 | 21 | def size(self): 22 | return len(self.items) 23 | -------------------------------------------------------------------------------- /chap3-BasicDataStructure/DequeLinkedList.py: -------------------------------------------------------------------------------- 1 | 2 | class Node: 3 | 4 | def __init__(self, data): 5 | self.data = data 6 | self.next = None 7 | 8 | 9 | class Deque: 10 | 11 | def __init__(self): 12 | self.head = None 13 | self.tail = None 14 | self.N = 0 15 | 16 | def __str__(self): 17 | plist = [] 18 | curr = self.head 19 | while curr is not None: 20 | plist.append(curr.data) 21 | curr = curr.next 22 | print (plist) 23 | 24 | def addFront(self, data): 25 | node = Node(data) 26 | node.next = self.head 27 | self.head = node 28 | if self.tail is None: 29 | self.tail = self.head 30 | self.N += 1 31 | 32 | def removeFront(self): 33 | if self.isEmpty(): 34 | print ("Deque is Empty!") 35 | return None 36 | node = self.head 37 | self.head = self.head.next 38 | node.next = None 39 | if self.head is None: 40 | self.tail = None 41 | self.N -= 1 42 | return node 43 | 44 | def addRear(self, data): 45 | node = Node(data) 46 | if self.isEmpty(): 47 | self.tail = node 48 | self.head = self.tail 49 | else: 50 | self.tail.next = node 51 | self.tail = self.tail.next 52 | self.N += 1 53 | 54 | def removeRear(self): 55 | if self.isEmpty(): 56 | print ("Deque is Empty!") 57 | return None 58 | # check if size is 1 59 | if self.size() == 1: 60 | node = self.tail 61 | self.tail = None 62 | self.head = None 63 | self.N -= 1 64 | return node 65 | curr = self.head 66 | while curr.next.next is not None: 67 | curr = curr.next 68 | node = curr.next 69 | self.tail = curr 70 | self.tail.next = None 71 | self.N -= 1 72 | return node 73 | 74 | def isEmpty(self): 75 | return self.N == 0 76 | 77 | def size(self): 78 | return self.N 79 | -------------------------------------------------------------------------------- /chap3-BasicDataStructure/DequeLinkedListClient.py: -------------------------------------------------------------------------------- 1 | from DequeLinkedList import Deque 2 | 3 | 4 | def testDeque(): 5 | dq = Deque() 6 | while True: 7 | print ("Choose Operation\n", 8 | "1 - AddFront(data)\n", 9 | "2 - RemoveFront()\n", 10 | "3 - AddRear(data)\n", 11 | "4 - RemoveRear()\n", 12 | "5 - Size()\n", 13 | "6 - Exit()") 14 | choice = input("Enter choice: ") 15 | if choice == '1': 16 | dq = addFront(dq) 17 | dq.__str__() 18 | elif choice == '2': 19 | dq = removeFront(dq) 20 | dq.__str__() 21 | elif choice == '3': 22 | dq = addRear(dq) 23 | dq.__str__() 24 | elif choice == '4': 25 | dq = removeRear(dq) 26 | dq.__str__() 27 | elif choice == '5': 28 | dequeSize(dq) 29 | dq.__str__() 30 | elif choice == '6': 31 | break 32 | else: 33 | print ("Bad Choice - please choose valid operation") 34 | continue 35 | 36 | 37 | def dequeSize(deque): 38 | print ("Deque Size: ", deque.size()) 39 | 40 | 41 | def removeRear(deque): 42 | node = deque.removeRear() 43 | if node: 44 | print ("Removed: ", node.data) 45 | else: 46 | print ("Removed: ", node) 47 | return deque 48 | 49 | 50 | def addRear(deque): 51 | data = input("Enter data: ") 52 | deque.addRear(data) 53 | return deque 54 | 55 | 56 | def removeFront(deque): 57 | node = deque.removeFront() 58 | if node is not None: 59 | print ("Removed: ", node.data) 60 | else: 61 | print ("Removed: ", node) 62 | return deque 63 | 64 | 65 | def addFront(deque): 66 | data = input("Enter data: ") 67 | deque.addFront(data) 68 | return deque 69 | 70 | 71 | def main(): 72 | testDeque() 73 | 74 | if __name__ == '__main__': 75 | main() 76 | -------------------------------------------------------------------------------- /chap3-BasicDataStructure/DoublyLinkedList.py: -------------------------------------------------------------------------------- 1 | 2 | class Node: 3 | 4 | def __init__(self, data): 5 | self.data = data 6 | self.front = None 7 | self.back = None 8 | 9 | 10 | class DoublyLinkedList: 11 | 12 | def __init__(self): 13 | self.head = None 14 | self.N = 0 15 | 16 | def __str__(self): 17 | dlList = [] 18 | curr = self.head 19 | count = 0 20 | while count < self.N: 21 | dlList.append(curr.data) 22 | curr = curr.front 23 | count += 1 24 | print (dlList) 25 | 26 | def search(self, key): 27 | if self.isEmpty(): 28 | print ("List is Empty") 29 | return -1 30 | index = 0 31 | found = False 32 | curr = self.head 33 | while index < self.N: 34 | if curr.data == key: 35 | found = True 36 | break 37 | index += 1 38 | curr = curr.front 39 | if found: 40 | return index 41 | return -1 42 | 43 | def removeIndex(self, index): 44 | if self.isEmpty(): 45 | print ("List is Empty!") 46 | return None 47 | if index < 0: 48 | index = index + self.N 49 | 50 | if (index < 0) or (index > (self.N - 1)): 51 | print ("Bad index value.. exiting") 52 | return None 53 | 54 | count = 0 55 | curr = self.head 56 | while count < index: 57 | count += 1 58 | curr = curr.front 59 | curr.front.back = curr.back 60 | curr.back.front = curr.front 61 | if index == 0: 62 | self.head = self.head.front 63 | curr.front = None 64 | curr.back = None 65 | self.N -= 1 66 | return curr 67 | 68 | def removeRear(self): 69 | if self.isEmpty(): 70 | print ("list is empty!") 71 | return None 72 | node = self.head.back 73 | self.head.back.back.front = self.head 74 | self.head.back = self.head.back.back 75 | node.front = None 76 | node.back = None 77 | self.N -= 1 78 | return node 79 | 80 | def removeFront(self): 81 | if self.isEmpty(): 82 | print ("list is empty!") 83 | return None 84 | node = self.head 85 | self.head.front.back = self.head.back 86 | self.head.back.front = self.head.front 87 | self.head = self.head.front 88 | node.front = None 89 | node.back = None 90 | self.N -= 1 91 | return node 92 | 93 | def insert(self, data, index): 94 | if index < 0 or index > self.N: 95 | print ("Bad index!") 96 | return False 97 | 98 | node = Node(data) 99 | 100 | # check if first node in DLL 101 | if self.head is None: 102 | self.head = node 103 | self.head.front = self.head 104 | self.head.back = self.head 105 | else: 106 | count = 0 107 | curr = self.head 108 | while (count < index-1): 109 | curr = curr.front 110 | count += 1 111 | node.back = curr 112 | node.front = curr.front 113 | curr.front.back = node 114 | curr.front = node 115 | if index == 0: 116 | self.head = node 117 | self.N += 1 118 | return True 119 | 120 | def addFront(self, data): 121 | node = Node(data) 122 | if self.isEmpty(): 123 | node.front = node 124 | node.back = node 125 | self.head = node 126 | else: 127 | node.front = self.head 128 | node.back = self.head.back 129 | self.head.back.front = node 130 | self.head.back = node 131 | self.head = node 132 | self.N += 1 133 | 134 | def addRear(self, data): 135 | node = Node(data) 136 | if self.isEmpty(): 137 | node.front = node 138 | node.back = node 139 | self.head = node 140 | else: 141 | node.back = self.head.back 142 | node.front = self.head 143 | self.head.back.front = node 144 | self.head.back = node 145 | self.N += 1 146 | 147 | def size(self): 148 | return self.N 149 | 150 | def isEmpty(self): 151 | return self.N == 0 152 | -------------------------------------------------------------------------------- /chap3-BasicDataStructure/DoublyLinkedListClient.py: -------------------------------------------------------------------------------- 1 | from DoublyLinkedList import DoublyLinkedList 2 | 3 | 4 | def testDoublyLinkedList(): 5 | dll = DoublyLinkedList() 6 | while True: 7 | print("Choose Operations:\n", 8 | "1 - addFront(data)\n", 9 | "2 - addRear(data)\n", 10 | "3 - insert(data, index)\n", 11 | "4 - removeFront()\n", 12 | "5 - removeRear()\n", 13 | "6 - remove(index)\n", 14 | "7 - search(key)\n", 15 | "8 - size()\n", 16 | "9 - exit()") 17 | choice = input("Enter Choice: ") 18 | if choice == '1': 19 | dll = addFront(dll) 20 | dll.__str__() 21 | elif choice == '2': 22 | dll = addRear(dll) 23 | dll.__str__() 24 | elif choice == '3': 25 | dll = insertDLL(dll) 26 | dll.__str__() 27 | elif choice == '4': 28 | dll = removeFront(dll) 29 | dll.__str__() 30 | elif choice == '5': 31 | dll = removeRear(dll) 32 | dll.__str__() 33 | elif choice == '6': 34 | dll = removeIndex(dll) 35 | dll.__str__() 36 | elif choice == '7': 37 | search(dll) 38 | dll.__str__() 39 | elif choice == '8': 40 | dll.__str__() 41 | print ("Doubly Linked List Size: ", dll.size()) 42 | elif choice == '9': 43 | break 44 | else: 45 | print ("Bad Choice. Choose valid operation!") 46 | continue 47 | 48 | 49 | def search(dll): 50 | key = input("Enter search key: ") 51 | index = dll.search(key) 52 | if index >= 0: 53 | print ("Key: %s -> found at index: %d" % (key, index)) 54 | else: 55 | print ("Key: %s -> Not Found in list" % key) 56 | 57 | 58 | def removeIndex(dll): 59 | index = input("Enter index: ") 60 | index = index.strip() 61 | if not index.isdigit(): 62 | print ("Bad Index Value") 63 | return dll 64 | index = int(index) 65 | node = dll.removeIndex(index) 66 | if node: 67 | print ("Removed: ", node.data) 68 | else: 69 | print ("Remove Failed!") 70 | return dll 71 | 72 | 73 | def removeRear(dll): 74 | node = dll.removeRear() 75 | if node: 76 | print ("Removed: ", node.data) 77 | else: 78 | print ("Rear remove failed!") 79 | return dll 80 | 81 | 82 | def removeFront(dll): 83 | node = dll.removeFront() 84 | if node: 85 | print ("Removed: ", node.data) 86 | else: 87 | print ("Front Remove Failed!") 88 | return dll 89 | 90 | 91 | def insertDLL(dll): 92 | data = input("Enter data: ") 93 | index = input("Enter index for data insert: ") 94 | index = index.strip() 95 | if not index.isdigit(): 96 | print ("Bad Index Data.. aborting insert") 97 | return dll 98 | index = int(index) 99 | result = dll.insert(data, index) 100 | if result: 101 | print ("Insert successful!") 102 | else: 103 | print ("Insert FAILED!") 104 | return dll 105 | 106 | 107 | def addRear(dll): 108 | data = input("Enter data: ") 109 | dll.addRear(data) 110 | return dll 111 | 112 | 113 | def addFront(dll): 114 | data = input("Enter data: ") 115 | dll.addFront(data) 116 | return dll 117 | 118 | 119 | def main(): 120 | testDoublyLinkedList() 121 | 122 | 123 | if __name__ == '__main__': 124 | main() 125 | -------------------------------------------------------------------------------- /chap3-BasicDataStructure/HTMLTagValidator.py: -------------------------------------------------------------------------------- 1 | from Stack import Stack 2 | 3 | 4 | def validateHTMLTags(htmlStr): 5 | stack = Stack() 6 | hsize = len(htmlStr) 7 | i = 0 8 | while i < hsize: 9 | tag = [] 10 | openTag = True 11 | if htmlStr[i] == '<': 12 | tag.append('<') 13 | i += 1 14 | if htmlStr[i] == '/': 15 | openTag = False 16 | i += 1 17 | while (i < hsize) and htmlStr[i] == ' ': 18 | i += 1 19 | while (i < hsize) and (htmlStr[i].isalpha() or htmlStr[i].isdigit()): 20 | tag.append(htmlStr[i]) 21 | i += 1 22 | while (i < hsize) and htmlStr[i] != '>': 23 | i += 1 24 | if (i >= hsize): 25 | return False 26 | tag.append(htmlStr[i]) 27 | htmTag = ''.join(tag) 28 | # print ("tag: ", htmTag) 29 | if openTag: 30 | stack.push(htmTag) 31 | elif stack.is_empty(): 32 | return False 33 | else: 34 | topTag = stack.pop() 35 | # print("popped: ", topTag) 36 | # print("htmTag: ", htmTag) 37 | if topTag != htmTag: 38 | return False 39 | i += 1 40 | if not stack.is_empty(): 41 | return False 42 | return True 43 | 44 | 45 | def readinput(): 46 | print ("Enter html string: ") 47 | htmlStr = input() 48 | return htmlStr 49 | 50 | 51 | def main(): 52 | html = readinput() 53 | isValid = validateHTMLTags(html) 54 | print("Input HTML String Valid? ", isValid) 55 | 56 | 57 | if __name__ == '__main__': 58 | main() 59 | -------------------------------------------------------------------------------- /chap3-BasicDataStructure/HTMLTagValidator_inout.txt: -------------------------------------------------------------------------------- 1 | Enter html string: 2 | Example

Hello, world

3 | Input HTML String Valid? True 4 | =========================================================================================== 5 | Enter html string: 6 | Example 7 | Input HTML String Valid? False 8 | =========================================================================================== 9 | Enter html string: 10 | Example 11 | Input HTML String Valid? True 12 | 13 | -------------------------------------------------------------------------------- /chap3-BasicDataStructure/HotPotato.py: -------------------------------------------------------------------------------- 1 | from Queue import Queue 2 | 3 | 4 | def hotPotato(players, num): 5 | simQ = Queue() 6 | for p in players: 7 | simQ.enqueue(p) 8 | 9 | while simQ.size() > 1: 10 | for i in range(num): 11 | simQ.enqueue(simQ.dequeue()) 12 | simQ.dequeue() 13 | return simQ.dequeue() 14 | 15 | 16 | def readInput(): 17 | print ("Enter player names, comma separated") 18 | names = input() 19 | while True: 20 | num = input("Enter count: ") 21 | if num.isdigit(): 22 | num = int(num) 23 | break 24 | else: 25 | print ("Bad Input. Please try again.") 26 | name_list = names.split(",") 27 | name_list = [n.strip() for n in name_list] 28 | return (name_list, num) 29 | 30 | 31 | def main(): 32 | players, num = readInput() 33 | winner = hotPotato(players, num) 34 | print ("Winner is: ", winner) 35 | 36 | 37 | if __name__ == '__main__': 38 | main() 39 | -------------------------------------------------------------------------------- /chap3-BasicDataStructure/HotPotatoRand.py: -------------------------------------------------------------------------------- 1 | from Queue import Queue 2 | import random 3 | 4 | 5 | def hotPotato(players): 6 | if players is None or len(players) <= 1: 7 | return players 8 | 9 | simQ = Queue() 10 | plsize = len(players) 11 | for p in players: 12 | simQ.enqueue(p) 13 | 14 | while simQ.size() > 1: 15 | r = random.randint(plsize, plsize*2) 16 | for i in range(r): 17 | simQ.enqueue(simQ.dequeue()) 18 | simQ.dequeue() 19 | return simQ.dequeue() 20 | 21 | 22 | def readInput(): 23 | print ("Enter player names, comma separated") 24 | names = input() 25 | # while True: 26 | # num = input("Enter count: ") 27 | # if num.isdigit(): 28 | # num = int(num) 29 | # break 30 | # else: 31 | # print ("Bad Input. Please try again.") 32 | name_list = names.split(",") 33 | name_list = [n.strip() for n in name_list] 34 | return (name_list) 35 | 36 | 37 | def main(): 38 | players = readInput() 39 | winner = hotPotato(players) 40 | print ("Winner is: ", winner) 41 | 42 | 43 | if __name__ == '__main__': 44 | main() 45 | -------------------------------------------------------------------------------- /chap3-BasicDataStructure/HotPotatoRand_inout.txt: -------------------------------------------------------------------------------- 1 | Enter player names, comma separated 2 | mani, sani, arch, shrey, john, rambo, troy, seema, reena, leena, peena 3 | Winner is: shrey 4 | =========================================================================================== 5 | Enter player names, comma separated 6 | mani, sani, arch, shrey, john, rambo, troy, seema, reena, leena, peena 7 | Winner is: john 8 | =========================================================================================== 9 | Enter player names, comma separated 10 | mani, sani, arch, shrey, john, rambo, troy, seema, reena, leena, peena 11 | Winner is: mani 12 | 13 | -------------------------------------------------------------------------------- /chap3-BasicDataStructure/InfixCalc.py: -------------------------------------------------------------------------------- 1 | from InfixToPostfix import infixToPostfix 2 | from InfixToPostfix import readInput 3 | from PostfixEval import postfixEvaluator 4 | 5 | 6 | def infixCalc(infixExpr): 7 | postfix = infixToPostfix(infixExpr) 8 | print ("Postfix: ", postfix) 9 | ans = postfixEvaluator(postfix) 10 | return ans 11 | 12 | 13 | def main(): 14 | infixExpr = readInput() 15 | ans = infixCalc(infixExpr) 16 | print ("Result: ", ans) 17 | 18 | 19 | if __name__ == '__main__': 20 | main() 21 | -------------------------------------------------------------------------------- /chap3-BasicDataStructure/InfixCalc_inout.txt: -------------------------------------------------------------------------------- 1 | Enter infix expression with space. 2 | infix expression: ( 2 + 3 ) ^ ( 1 + 4 ) 3 | token list: ['(', '2', '+', '3', ')', '^', '(', '1', '+', '4', ')'] 4 | Postfix: 2 3 + 1 4 + ^ 5 | Result: 3125 6 | =========================================================================== 7 | Enter infix expression with space. 8 | infix expression: ( 2 + 4 ) * 3 9 | token list: ['(', '2', '+', '4', ')', '*', '3'] 10 | Postfix: 2 4 + 3 * 11 | Result: 18 12 | 13 | -------------------------------------------------------------------------------- /chap3-BasicDataStructure/InfixToPostfix.py: -------------------------------------------------------------------------------- 1 | from Stack import Stack 2 | from ParenChecker import parenChecker 3 | 4 | 5 | def infixToPostfix(tokenList): 6 | opStack = Stack() 7 | prec = {"^": 4, "*": 3, "/": 3, "+": 2, "-": 2, "(": 1} 8 | postfixList = [] 9 | # tokenList = list(infix.split(" ")) 10 | charTokens = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 11 | numTokens = "1234567890" 12 | 13 | print ("token list: ", tokenList) 14 | for token in tokenList: 15 | if token in charTokens or token in numTokens or token.isdigit(): 16 | postfixList.append(token) 17 | elif token == "(": 18 | opStack.push(token) 19 | elif token == ")": 20 | topToken = opStack.pop() 21 | while topToken != "(": 22 | postfixList.append(topToken) 23 | topToken = opStack.pop() 24 | else: 25 | while (not opStack.is_empty()) and \ 26 | (prec[opStack.peek()] >= prec[token]): 27 | postfixList.append(opStack.pop()) 28 | opStack.push(token) 29 | while not opStack.is_empty(): 30 | postfixList.append(opStack.pop()) 31 | 32 | return ' '.join(postfixList) 33 | 34 | 35 | def validateInput(expr): 36 | if not expr: 37 | return False 38 | expr = expr.strip() 39 | 40 | parenList = [] 41 | for e in expr: 42 | if e == '(' or e == ')': 43 | parenList.append(e) 44 | parenExpr = ''.join(parenList) 45 | if not parenChecker(parenExpr): 46 | return False 47 | tokenList = expr.split(" ") 48 | sanitizedTokList = [] 49 | for t in tokenList: 50 | t = t.strip() 51 | sanitizedTokList.append(t) 52 | if t.isdigit(): 53 | continue 54 | elif t == '+' or t == '-' or t == '/' or t == '*' or t == '^': 55 | continue 56 | elif t == '(' or t == ')': 57 | continue 58 | else: 59 | return False 60 | return sanitizedTokList 61 | 62 | 63 | def readInput(): 64 | print ("Enter infix expression with space.") 65 | infix = input("infix expression: ") 66 | tokList = validateInput(infix) 67 | while not tokList: 68 | print ("Bad Input! Please enter valid infix expression") 69 | print ("Enter infix expression with space.") 70 | infix = input("infix expression: ") 71 | 72 | return tokList 73 | 74 | 75 | def main(): 76 | infix = readInput() 77 | postfix = infixToPostfix(infix) 78 | print ("Postfix expression: ", postfix) 79 | 80 | 81 | if __name__ == '__main__': 82 | main() 83 | -------------------------------------------------------------------------------- /chap3-BasicDataStructure/InfixToPostfix_inout.txt: -------------------------------------------------------------------------------- 1 | Enter infix expression with space. 2 | infix expression: 10 + 3 * 5 / ( 16 - 4 ) 3 | token list: ['10', '+', '3', '*', '5', '/', '(', '16', '-', '4', ')'] 4 | Postfix expression: 10 3 5 * 16 4 - / + 5 | =========================================================================== 6 | Enter infix expression with space. 7 | infix expression: 5 * 3 ^ ( 4 - 2 ) 8 | token list: ['5', '*', '3', '^', '(', '4', '-', '2', ')'] 9 | Postfix expression: 5 3 4 2 - ^ * 10 | 11 | 12 | -------------------------------------------------------------------------------- /chap3-BasicDataStructure/LinkedList.py: -------------------------------------------------------------------------------- 1 | from Node import Node 2 | 3 | 4 | class LinkedList: 5 | 6 | def __init__(self): 7 | self.head = None 8 | self.N = 0 9 | 10 | def __str__(self): 11 | llist = [] 12 | curr = self.head 13 | while curr is not None: 14 | llist.append(curr.data) 15 | curr = curr.next 16 | print (llist) 17 | 18 | def search(self, key): 19 | curr = self.head 20 | while curr is not None: 21 | if curr.data == key: 22 | return True 23 | curr = curr.next 24 | return False 25 | 26 | def add(self, ndata): 27 | node = Node(ndata) 28 | if self.head is None: 29 | self.head = node 30 | else: 31 | node.next = self.head 32 | self.head = node 33 | self.N += 1 34 | 35 | def remove(self, ndata): 36 | curr = self.head 37 | prev = None 38 | found = False 39 | while curr is not None: 40 | if curr.data == ndata: 41 | found = True 42 | break 43 | else: 44 | prev = curr 45 | curr = curr.next 46 | if found: 47 | if prev is None: 48 | self.head = self.head.next 49 | else: 50 | prev.next = curr.next 51 | curr.next = None 52 | self.N -= 1 53 | return found 54 | 55 | def size(self): 56 | return self.N 57 | 58 | def isEmtpy(self): 59 | return self.N == 0 60 | -------------------------------------------------------------------------------- /chap3-BasicDataStructure/LinkedListClient.py: -------------------------------------------------------------------------------- 1 | from LinkedList import LinkedList 2 | 3 | 4 | def testLinkedList(): 5 | ll = LinkedList() 6 | while True: 7 | print ("Choose operation: ") 8 | print (" 1 - Add\n", 9 | "2 - Remove\n", 10 | "3 - Search\n", 11 | "4 - Size\n", 12 | "5 - Exit\n") 13 | choice = input() 14 | if choice == '1': 15 | ll = addll(ll) 16 | ll.__str__() 17 | elif choice == '2': 18 | ll == removell(ll) 19 | ll.__str__() 20 | elif choice == '3': 21 | searchKey(ll) 22 | ll.__str__() 23 | elif choice == '4': 24 | size(ll) 25 | ll.__str__() 26 | elif choice == '5': 27 | break 28 | else: 29 | print ("BAD Choice! Choose from 1 to 4 numbers") 30 | 31 | 32 | def addll(ll): 33 | print ("Enter data: ") 34 | data = input() 35 | ll.add(data) 36 | return ll 37 | 38 | 39 | def removell(ll): 40 | print ("Data to remove: ") 41 | data = input() 42 | result = ll.remove(data) 43 | if result: 44 | print("REMOVED: ", data) 45 | else: 46 | print("Not Found in linked list!") 47 | 48 | 49 | def searchKey(ll): 50 | print ("Enter data to be searched: ") 51 | data = input() 52 | result = ll.search(data) 53 | if result: 54 | print ("FOUND!") 55 | else: 56 | print ("Not Found!") 57 | 58 | 59 | def size(ll): 60 | print("Size: ", ll.size()) 61 | 62 | 63 | def main(): 64 | testLinkedList() 65 | 66 | if __name__ == '__main__': 67 | main() 68 | -------------------------------------------------------------------------------- /chap3-BasicDataStructure/Node.py: -------------------------------------------------------------------------------- 1 | 2 | class Node: 3 | 4 | def __init__(self, data): 5 | self.data = data 6 | self.next = None 7 | 8 | def getData(self): 9 | return self.data 10 | 11 | def setData(self, data): 12 | self.data = data 13 | 14 | def getNext(self): 15 | return self.next 16 | 17 | def setNext(self, next): 18 | self.next = next 19 | -------------------------------------------------------------------------------- /chap3-BasicDataStructure/OrderedList.py: -------------------------------------------------------------------------------- 1 | from Node import Node 2 | 3 | 4 | class OrderedList: 5 | 6 | def __init__(self): 7 | self.head = None 8 | 9 | def __str__(self): 10 | ol = [] 11 | curr = self.head 12 | while curr is not None: 13 | ol.append(curr.data) 14 | curr = curr.next 15 | print (ol) 16 | 17 | def isEmpty(self): 18 | return self.head is None 19 | 20 | def popPos(self, key): 21 | if self.isEmpty(): 22 | print ("List is EMPTY!") 23 | return False 24 | front = self.head 25 | prev = None 26 | count = 0 27 | node = None 28 | found = False 29 | while front is not None: 30 | if count == key: 31 | found = True 32 | break 33 | count += 1 34 | prev = front 35 | front = front.next 36 | if not found: 37 | return node 38 | 39 | if prev is None: 40 | node = self.head 41 | self.head = self.head.next 42 | else: 43 | node = front 44 | prev.next = front.next 45 | front.next = None 46 | return node 47 | 48 | def pop(self): 49 | if self.isEmpty(): 50 | print ("List is EMPTY!") 51 | return None 52 | 53 | # check if the list has single node 54 | if self.head.next is None: 55 | node = self.head 56 | self.head = None 57 | return node 58 | 59 | front = self.head 60 | back = None 61 | while front.next is not None: 62 | back = front 63 | front = front.next 64 | node = front 65 | back.next = front.next 66 | return node 67 | 68 | def search(self, key): 69 | if self.isEmpty(): 70 | print ("List is EMPTY!") 71 | return -1 72 | current = self.head 73 | index = 0 74 | 75 | while not (current is None): 76 | if current.getData() == key: 77 | return index 78 | elif key < current.getData(): 79 | break 80 | current = current.getNext() 81 | index += 1 82 | return -1 83 | 84 | def size(self): 85 | current = self.head 86 | count = 0 87 | while not (current is None): 88 | count += 1 89 | current = current.getNext() 90 | return count 91 | 92 | def add(self, data): 93 | current = self.head 94 | prev = None 95 | while not (current is None): 96 | if current.getData() > data: 97 | break 98 | else: 99 | prev = current 100 | current = current.getNext() 101 | temp = Node(data) 102 | if prev is None: 103 | temp.setNext(self.head) 104 | self.head = temp 105 | else: 106 | temp.setNext(current) 107 | prev.setNext(temp) 108 | 109 | def remove(self, data): 110 | if self.isEmpty(): 111 | print ("List is EMPTY!") 112 | return None 113 | current = self.head 114 | prev = None 115 | found = False 116 | 117 | while not found: 118 | if current.getData() == data: 119 | found = True 120 | else: 121 | prev = current 122 | current = current.getNext() 123 | node = None 124 | if prev is None: 125 | node = self.head 126 | self.head = current.getNext() 127 | else: 128 | node = current 129 | prev.setNext(current.getNext()) 130 | return node 131 | -------------------------------------------------------------------------------- /chap3-BasicDataStructure/OrderedListClient.py: -------------------------------------------------------------------------------- 1 | from OrderedList import OrderedList 2 | 3 | 4 | def testOrderedList(): 5 | ol = OrderedList() 6 | while True: 7 | print ("Choose operation: ") 8 | print (" 1 - Add(data)\n", 9 | "2 - Pop()\n", 10 | "3 - Search(key)\n", 11 | "4 - Remove(data)\n", 12 | "5 - Size()\n", 13 | "6 - Pop(pos)\n", 14 | "7 - exit()\n", 15 | ) 16 | choice = input() 17 | if choice == '1': 18 | ol = add(ol) 19 | ol.__str__() 20 | elif choice == '2': 21 | ol = pop(ol) 22 | ol.__str__() 23 | elif choice == '3': 24 | search(ol) 25 | ol.__str__() 26 | elif choice == '4': 27 | ol = remove(ol) 28 | ol.__str__() 29 | elif choice == '5': 30 | print ("Size of list: %d" % ol.size()) 31 | ol.__str__() 32 | elif choice == '6': 33 | ol = popPos(ol) 34 | ol.__str__() 35 | elif choice == '7': 36 | break 37 | else: 38 | print ("Bad Choice - choose from valid options") 39 | 40 | 41 | def remove(ol): 42 | data = input("Enter data to be removed: ") 43 | node = ol.remove(data) 44 | if node: 45 | print ("Removed: ", node.data) 46 | else: 47 | print ("Data: %s NOT FOUND in the list" % data) 48 | return ol 49 | 50 | 51 | def search(ol): 52 | key = input("Enter search key: ") 53 | index = ol.search(key) 54 | if index < 0: 55 | print ("Key: %s NOT FOUND!" % key) 56 | else: 57 | print ("Key: %s Found at index: %d" % (key, index)) 58 | 59 | 60 | def popPos(ol): 61 | key = input("Enter position to be popped: ") 62 | key = key.strip() 63 | if not key.isdigit(): 64 | print ("Bad position data") 65 | return ol 66 | key = int(key) 67 | node = ol.popPos(key) 68 | if node: 69 | print ("Popped: ", node.data) 70 | else: 71 | print ("Popped: None") 72 | return ol 73 | 74 | 75 | def pop(ol): 76 | node = ol.pop() 77 | if node: 78 | print ("Popped: ", node.data) 79 | else: 80 | print ("Popped: None") 81 | return ol 82 | 83 | 84 | def add(ol): 85 | data = input("Enter data to be added: ") 86 | ol.add(data) 87 | return ol 88 | 89 | 90 | def main(): 91 | testOrderedList() 92 | 93 | if __name__ == '__main__': 94 | main() 95 | -------------------------------------------------------------------------------- /chap3-BasicDataStructure/Palindrome.py: -------------------------------------------------------------------------------- 1 | from Deque import Deque 2 | 3 | 4 | def isPalindrome(word): 5 | if word is None: 6 | return False 7 | if len(word) <= 1: 8 | return True 9 | 10 | DQ = Deque() 11 | for w in word: 12 | DQ.addRear(w) 13 | 14 | while (DQ.size() > 1): 15 | front = DQ.removeFront() 16 | rear = DQ.removeRear() 17 | if front != rear: 18 | return False 19 | return True 20 | 21 | 22 | def readInput(): 23 | inp = input("Enter string: ") 24 | return inp 25 | 26 | 27 | def main(): 28 | word = readInput() 29 | print ("Is \"{}\" a palindrome: {}".format(word, isPalindrome(word))) 30 | 31 | 32 | if __name__ == '__main__': 33 | main() 34 | -------------------------------------------------------------------------------- /chap3-BasicDataStructure/ParenChecker.py: -------------------------------------------------------------------------------- 1 | from Stack import Stack 2 | 3 | 4 | def parenChecker(paren): 5 | """ This method checks if the parenthesis is balanced or not. 6 | Example could be: 7 | ()(), (()), (())() are all balanced. 8 | ((), ((()()) are not balanced """ 9 | S = Stack() 10 | balanced = True 11 | index = 0 12 | while index < len(paren): 13 | if paren[index] == '(': 14 | S.push(paren[index]) 15 | else: 16 | if S.is_empty(): 17 | balanced = False 18 | break 19 | else: 20 | S.pop() 21 | index += 1 22 | if not S.is_empty(): 23 | balanced = False 24 | return balanced 25 | 26 | 27 | def readInput(): 28 | while True: 29 | flag = True 30 | paren = input("Enter parenthesis string: ") 31 | for i in range(len(paren)): 32 | if paren[i] != '(' and paren[i] != ')': 33 | print ("Bad Input. Please try again..") 34 | flag = False 35 | break 36 | if flag: 37 | break 38 | return paren 39 | 40 | 41 | def main(): 42 | paren = readInput() 43 | print ("Parenthesis string: ", paren) 44 | print ("Are parenthesis balanced: ", parenChecker(paren)) 45 | 46 | 47 | if __name__ == '__main__': 48 | main() 49 | -------------------------------------------------------------------------------- /chap3-BasicDataStructure/PostfixEval.py: -------------------------------------------------------------------------------- 1 | from Stack import Stack 2 | 3 | 4 | def postfixEvaluator(pfix): 5 | pfixList = pfix.split(" ") 6 | opStack = Stack() 7 | 8 | for tok in pfixList: 9 | if tok.isdigit(): 10 | opStack.push(int(tok)) 11 | else: 12 | if opStack.size() < 2: 13 | print ("Invalid postfix expression") 14 | return None 15 | second = opStack.pop() 16 | first = opStack.pop() 17 | ans = doMath(tok, first, second) 18 | if ans is None: 19 | return ans 20 | opStack.push(ans) 21 | return opStack.pop() 22 | 23 | 24 | def doMath(operator, x, y): 25 | if operator == '*': 26 | return (x * y) 27 | elif operator == "+": 28 | return (x + y) 29 | elif operator == '-': 30 | return (x - y) 31 | elif operator == "/": 32 | if y == 0: 33 | return None 34 | return (float(x)/y) 35 | elif operator == '^': 36 | if x == 0: 37 | return None 38 | elif y == 0: 39 | return 1 40 | else: 41 | return x ** y 42 | return None 43 | 44 | 45 | def readInput(): 46 | print ("Enter postfix expression separated by space.") 47 | pfix = input("Expression: ") 48 | return pfix 49 | 50 | 51 | def main(): 52 | postfix = readInput() 53 | result = postfixEvaluator(postfix) 54 | print("Result: ", result) 55 | 56 | 57 | if __name__ == '__main__': 58 | main() 59 | -------------------------------------------------------------------------------- /chap3-BasicDataStructure/Programming_Exercises_chap3.md: -------------------------------------------------------------------------------- 1 | ## Programming Exercises - Chapter-3: Basic Data Structures 2 | ===================================================================== 3 | 4 | ##### 1. Modify the infix-to-postfix algorithm so that it can handle errors. 5 | > Please refer to InfixToPostfix.py - very generic question - I have added basic error checking. 6 | 7 | ##### 2. Modify the postfix evaluation algorithm so that it can handle errors. 8 | > Please refer to PostfixEval.py - Though there are basic error handling checks - see if you can extend it. 9 | 10 | ##### 3. Implement a direct infix evaluator that combines the functionality of infix-to-postfix conversion and the postfix evaluation algorithm. Your evaluator should process infix tokens from left to right and use two stacks, one for operators and one for operands, to perform the evaluation. 11 | > Please refer to InfixCalc.py for implementation and InfixCalc_inout.txt for sample input/output. 12 | 13 | ##### 4. Turn your direct infix evaluator from the previous problem into a calculator. 14 | > Please refer to Calculator.py, Tokenizer.py and Calculator_inout.py for implementation, and sample input/output. 15 | 16 | ##### 5. Implement the Queue ADT, using a list such that the rear of the queue is at the end of the list. 17 | > Refer to QueueADT.py and QueueADTClient.py for implementation and sample input/output 18 | 19 | ##### 6. Design and implement an experiment to do benchmark comparisons of the two queue implementations. What can you learn from such an experiment? 20 | > Refer to QueueADTClient.py and QueueADTClient_inout.txt. we learn that with List implementation one of the enqueue/dequeue operation would be O(n) and if we need both enqueue/dequeue operation be O(1) then we need to resort to linked list implementation of Queue. 21 | 22 | ##### 7. It is possible to implement a queue such that both enqueue and dequeue have O(1) performance on average. In this case it means that most of the time enqueue and dequeue will be O(1) except in one particular circumstance where dequeue will be O(n). 23 | > Please refer to QueueLinkedList.py and QueueLinkedListClient.py for the linked list implementation of Queue. Here both enqueue and dequeue operations are O(1) 24 | 25 | ##### 9. Modify the Hot Potato simulation to allow for a randomly chosen counting value so that each pass is not predictable from the previous one. 26 | > Please refer to HotPotatoRand.py for implementation and HotPotatoRand_inout.txt for input/output examples 27 | 28 | ##### 10. Implement a radix sorting machine. A radix sort for base 10 integers is a mechanical sorting technique that utilizes a collection of bins, one main bin and 10 digit bins. Each bin acts like a queue and maintains its values in the order that they arrive. The algorithm begins by placing each number in the main bin. Then it considers each value digit by digit. The first value is removed and placed in a digit bin corresponding to the digit being considered. For example, if the ones digit is being considered, 534 is placed in digit bin 4 and 667 is placed in digit bin 7. Once all the values are placed in the corresponding digit bins, the values are collected from bin 0 to bin 9 and placed back in the main bin. The process continues with the tens digit, the hundreds, and so on. After the last digit is processed, the main bin contains the values in order. 29 | > Refer to RadixSort.py for implementation and RadixSort_inout.txt for sample input/output. 30 | 31 | ##### 11. Another example of the parentheses matching problem comes from hypertext markup language (HTML). In HTML, tags exist in both opening and closing forms and must be balanced to properly describe a web document. This very simple HTML document: 32 | ``` html 33 | 34 | 35 | 36 | Example 37 | 38 | 39 | 40 |

Hello, world

41 | 42 | 43 | ``` 44 | is intended only to show the matching and nesting structure for tags in the language. Write a program that can check an HTML document for proper opening and closing tags. 45 | > Please refer to HTMLTagValidator.py for implementation and HTMLTagValidator_inout.txt for sample input/output. 46 | 47 | ##### 12. To implement the length method, we counted the number of nodes in the list. An alter- native strategy would be to store the number of nodes in the list as an additional piece of data in the head of the list. Modify the UnorderedList class to include this information and rewrite the length method. 48 | > Please refer to LinkedList.py for unordered link list implementation and LinkedListClient.py for testing the main class LinkedList.py 49 | 50 | ##### 13. Implement the remove method so that it works correctly in the case where the item is not in the list. 51 | > refer to remove method in LinkedList.py and LinkedListClient.py testing methods. 52 | 53 | ##### 14. Modify the list classes to allow duplicates. Which methods will be impacted by this change? 54 | ##### 15. Implement the \'__str__\' method in the UnorderedList class. What would be a good string representation for a list? 55 | ##### 16. Implement \'__str__\' method so that lists are displayed the Python way (with square brackets). 56 | > LinkedList.py has \'__str__\' method which represents(prints) the LinkedList like python lists and LinkedList.py allows for duplicates nodes as well - remove method deletes the first first occurance of the data from head if found. 57 | 58 | ##### 17. Implement the remaining operations defined in the UnorderedList ADT (append, index, pop, insert). 59 | > Refer to SLList.py for implementation of append, index, pop and insert. Test client code is written in SLListClient.py 60 | 61 | ##### 18. Implement a slice method for the UnorderedList class. It should take two parameters, start and stop, and return a copy of the list starting at the start position and going up to but not including the stop position. 62 | > Please refer the slice(start, stop) method in SLList.py - testing method has been added in SLListClient.py 63 | 64 | ##### 19. Implement the remaining operations defined in the OrderedList ADT. 65 | > Please refer to OrderedList.py for operations implementation and OrderedListClient.py for testing OrderedList.py class 66 | 67 | ##### 20. Implement a stack using linked lists. 68 | > Please refer to StackLinkedList.py for implementation and StackLinkedListClient.py for test client code. 69 | 70 | ##### 21. Implement a queue using linked lists. 71 | > Please refer to QueueLinkedList.py for implementation and QueueLinkedListClient.py for test client code. 72 | 73 | ##### 22. Implement a deque using linked lists. 74 | > Please refer to DequeLinkedList.py for Deque implementation and DequeLinkedListClient.py for client test code 75 | 76 | ###### 23. Design and implement an experiment that will compare the performance of a Python list with a list implemented as a linked list. 77 | > Approach: List in python has array implementation - what this means is elements of list are stored in contiguous memory locations. So operations like index(), search()(binary search - if data is sorted), sort() can be really efficient. Cons here is when the list is full and we need to add more data to it, python has to copy the old list to a bigger contiguous space, similarly when we have removed a lot of elements from the list we need to resize() it to smaller size - this again requires copy to smaller contiguous memory locations. Experimentation is left as an exercise to user :) 78 | 79 | ###### 24. Design and implement an experiment that will compare the performance of the Python list based stack and queue with the linked list implementation. 80 | > In Contrast Linked List implementation works really well when we need to add elements as we never have to copy the whole list (unlike List in python), or when we need to remove lot of elements from the linked list - This implementation ensures optimal usage of memory. Though there are associated overheads of maintaining pointers. Indexing and Search (BinarySearch) can be challenging and deteriorate performance when implemented in linked list. Experimentation is left as an exercise :) 81 | 82 | ##### 25. The linked list implementation given above is called a singly linked list because each node has a single reference to the next node in sequence. An alternative implementation is known as a doubly linked list. In this implementation, each node has a reference to the next node (commonly called next) as well as a reference to the preceding node (commonly called back). The head reference also contains two references, one to the first node in the linked list and one to the last. Code this implementation in Python. 83 | > Please refer to DoublyLinkedList.py file for implementation and APIs and DoublyLinkedListClient.py for client test code. 84 | 85 | ##### 26. Create an implementation of a queue that would have an average performance of O(1) for enqueue and dequeue operations. 86 | > QueueLinkedList.py has implementation with front and back pointers which keep track of the head and tail of the queue - this helps in implementing Enqueu and Dequeue operations in O(1). Please refer to QueueLinkedListClient.py for client test code. 87 | -------------------------------------------------------------------------------- /chap3-BasicDataStructure/QClient.py: -------------------------------------------------------------------------------- 1 | from Queue import Queue 2 | 3 | 4 | def testQueue(): 5 | q = Queue() 6 | q.enqueue('hello') 7 | q.enqueue('dog') 8 | q.enqueue(3) 9 | print ("Deq: ", q.dequeue()) 10 | while not q.is_empty(): 11 | print (q.dequeue()) 12 | 13 | 14 | def main(): 15 | testQueue() 16 | 17 | 18 | if __name__ == '__main__': 19 | main() 20 | -------------------------------------------------------------------------------- /chap3-BasicDataStructure/QNode.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | class Node: 4 | 5 | def __init__(self, data): 6 | self.data = data 7 | self.next = None 8 | 9 | def getData(self): 10 | return self.data 11 | 12 | def setData(self, data): 13 | self.data = data 14 | 15 | def getNext(self): 16 | return self.next 17 | 18 | def setNext(self, next): 19 | self.next = next 20 | -------------------------------------------------------------------------------- /chap3-BasicDataStructure/Queue.py: -------------------------------------------------------------------------------- 1 | # Implementation of Queue ADT 2 | 3 | 4 | class Queue: 5 | 6 | def __init__(self): 7 | self.items = [] 8 | 9 | def is_empty(self): 10 | return self.items == [] 11 | 12 | def enqueue(self, value): 13 | self.items.insert(0, value) 14 | 15 | def dequeue(self): 16 | return self.items.pop() 17 | 18 | def size(self): 19 | return len(self.items) 20 | -------------------------------------------------------------------------------- /chap3-BasicDataStructure/QueueADT.py: -------------------------------------------------------------------------------- 1 | # Implementation of Queue ADT 2 | 3 | 4 | class Queue: 5 | 6 | def __init__(self): 7 | self.items = [] 8 | 9 | def is_empty(self): 10 | return self.items == [] 11 | 12 | def enqueue(self, value): 13 | self.items.append(value) 14 | 15 | def dequeue(self): 16 | return self.items.pop(0) 17 | 18 | def size(self): 19 | return len(self.items) 20 | -------------------------------------------------------------------------------- /chap3-BasicDataStructure/QueueADTClient.py: -------------------------------------------------------------------------------- 1 | from QueueADT import Queue 2 | import datetime as dt 3 | import time 4 | 5 | 6 | def testQueueADT(): 7 | Q = Queue() 8 | times = [] 9 | NUMS = [1000, 10000, 100000, 1000000] 10 | for N in NUMS: 11 | Q = Queue() 12 | start = dt.datetime.now() 13 | for i in range(N): 14 | Q.enqueue(i) 15 | end = dt.datetime.now() 16 | elapsed = ((end - start).microseconds*1000)/N 17 | print ("Enqueue for %d items: %d" % (1000, elapsed)) 18 | 19 | print ("==================================================") 20 | times = [] 21 | for N in NUMS: 22 | Q = Queue() 23 | for i in range(N): 24 | Q.enqueue(i) 25 | start = dt.datetime.now() 26 | for i in range(N): 27 | Q.dequeue() 28 | end = dt.datetime.now() 29 | elapsed = ((end - start).microseconds*1000)/N 30 | print ("Dequeue for %d items: %d" % (1000, elapsed)) 31 | 32 | 33 | def main(): 34 | testQueueADT() 35 | 36 | if __name__ == '__main__': 37 | main() 38 | -------------------------------------------------------------------------------- /chap3-BasicDataStructure/QueueADTClient_inout.txt: -------------------------------------------------------------------------------- 1 | Enqueue for 1000 items: 570 2 | Enqueue for 1000 items: 543 3 | Enqueue for 1000 items: 558 4 | Enqueue for 1000 items: 551 5 | ================================================== 6 | Dequeue for 1000 items: 970 7 | Dequeue for 1000 items: 3880 8 | Dequeue for 1000 items: 3726 9 | Dequeue for 1000 items: 82 10 | 11 | -------------------------------------------------------------------------------- /chap3-BasicDataStructure/QueueLinkedList.py: -------------------------------------------------------------------------------- 1 | from QNode import Node 2 | 3 | 4 | class Queue: 5 | 6 | def __init__(self): 7 | self.front = None 8 | self.back = None 9 | self.N = 0 10 | 11 | def enqueue(self, data): 12 | temp = Node(data) 13 | if self.front is None: 14 | self.front = temp 15 | self.back = self.front 16 | else: 17 | self.back.setNext(temp) 18 | self.back = self.back.getNext() 19 | self.N += 1 20 | 21 | def dequeue(self): 22 | if self.isEmpty(): 23 | print ("Queue is EMPTY!") 24 | return None 25 | temp = self.front 26 | if self.front.getNext() is None: 27 | self.front = None 28 | self.back = None 29 | else: 30 | self.front = self.front.getNext() 31 | self.N -= 1 32 | return temp 33 | 34 | def qprint(self): 35 | if not self.isEmpty(): 36 | node = self.front 37 | while not node is None: 38 | print (node.data, end=" -> ") 39 | node = node.getNext() 40 | print("null") 41 | else: 42 | print ("Queue is EMPTY!") 43 | 44 | 45 | def isEmpty(self): 46 | return self.N == 0 47 | 48 | def size(self): 49 | return self.N 50 | -------------------------------------------------------------------------------- /chap3-BasicDataStructure/QueueLinkedListClient.py: -------------------------------------------------------------------------------- 1 | from QueueLinkedList import Queue 2 | 3 | 4 | def testQueueLinkedList(): 5 | Q = Queue() 6 | while True: 7 | print ("Choose operation: ") 8 | print (" 1 - Enqueue\n", 9 | "2 - Dequeue\n", 10 | "3 - Size\n", 11 | "4 - Exit\n") 12 | choice = input() 13 | if choice == '1': 14 | Q = enqueue(Q) 15 | elif choice == '2': 16 | Q = dequeue(Q) 17 | elif choice == '3': 18 | getSize(Q) 19 | elif choice == '4': 20 | break 21 | else: 22 | print ("BAD Choice! Choose from 1 to 4 numbers") 23 | 24 | 25 | def dequeue(Q): 26 | if not Q.isEmpty(): 27 | node = Q.dequeue() 28 | print ("Dequeued: ", node.data) 29 | Q.qprint() 30 | else: 31 | print ("Queue is EMPTY!") 32 | return Q 33 | 34 | 35 | def getSize(Q): 36 | size = Q.size() 37 | print ("Size of Queue: ", size) 38 | 39 | 40 | def enqueue(Q): 41 | data = input("Data to be enqueued: ") 42 | if (data is not None) or len(data) > 0: 43 | Q.enqueue(data) 44 | Q.qprint() 45 | else: 46 | print ("Bad data. Plz try again..") 47 | return Q 48 | 49 | 50 | def main(): 51 | testQueueLinkedList() 52 | 53 | 54 | if __name__ == '__main__': 55 | main() 56 | -------------------------------------------------------------------------------- /chap3-BasicDataStructure/RadixSort.py: -------------------------------------------------------------------------------- 1 | from Queue import Queue 2 | 3 | 4 | def radixSort(nums): 5 | """ we are taking the input as list of numbers (in string format) 6 | reason being - it will ease in indexing and getting the individual digits 7 | with the help of string utilities. 8 | Return - we return a list of numbers which are sorted in increasing order""" 9 | if nums is None or len(nums) < 1: 10 | return nums 11 | mainQ = Queue() 12 | binQs = [] 13 | # Add all the numbers in the main Queue 14 | for n in nums: 15 | mainQ.enqueue(n) 16 | # create an array of Queues and initialize them - bin 0 to 9 17 | for i in range(10): 18 | binQs.append(Queue()) 19 | # get the max length of digits in any input number - this will decide the 20 | # outer iteration we need to do in our radix sort implementation 21 | maxLen = len(nums[0]) 22 | for i in range(1, len(nums)): 23 | if len(nums[i]) > maxLen: 24 | maxLen = len(nums[i]) 25 | # we need to iterate maxLen times ie length of the biggest number (in 26 | # digits) 27 | for i in range(1, maxLen+1): 28 | visited = [] 29 | # below iteration includes only numbers of digit length i 30 | while not mainQ.is_empty(): 31 | val = mainQ.dequeue() 32 | if i > len(val): 33 | visited.append(val) 34 | continue 35 | r = val[-i] #get the ith index from last 36 | r = int(r) 37 | binQs[r].enqueue(val) 38 | for v in visited: 39 | mainQ.enqueue(v) 40 | for i in range(10): 41 | while not binQs[i].is_empty(): 42 | mainQ.enqueue(binQs[i].dequeue()) 43 | result = [] 44 | while not mainQ.is_empty(): 45 | result.append(mainQ.dequeue()) 46 | return result 47 | 48 | 49 | def readInput(): 50 | while True: 51 | print ("Enter comma separated numbers: ") 52 | data = input() 53 | data = data.split(",") 54 | try: 55 | nums = [d.strip() for d in data] 56 | break 57 | except ValueError: 58 | print ("Bad input, plz try again..") 59 | return nums 60 | 61 | 62 | def printData(nums): 63 | if nums is None or len(nums) < 1: 64 | print ("EMPTY LIST - Nothing to print!") 65 | else: 66 | for n in nums: 67 | print (n, end=" ") 68 | print() 69 | 70 | 71 | def main(): 72 | nums = readInput() 73 | nums = radixSort(nums) 74 | print ("After applying radix sort: ") 75 | printData(nums) 76 | 77 | 78 | if __name__ == '__main__': 79 | main() 80 | -------------------------------------------------------------------------------- /chap3-BasicDataStructure/RadixSort_inout.txt: -------------------------------------------------------------------------------- 1 | Enter comma separated numbers: 2 | 9, 8, 7, 6, 5, 4, 3, 2, 1, 11, 21, 31, 41, 19, 18, 17, 16, 15, 141, 0999 3 | After applying radix sort: 4 | 1 2 3 4 5 6 7 8 9 11 15 16 17 18 19 21 31 41 141 0999 5 | -------------------------------------------------------------------------------- /chap3-BasicDataStructure/SLList.py: -------------------------------------------------------------------------------- 1 | from Node import Node 2 | 3 | 4 | class LinkedList: 5 | 6 | def __init__(self): 7 | self.head = None 8 | self.tail = None 9 | self.N = 0 10 | 11 | def slice(self, start, stop): 12 | """ return a copy of the list starting at start position 13 | and going upto but not including stop position """ 14 | if (start < 1) or (start > stop) or (stop > self.N + 1): 15 | print ("Invalid range.") 16 | return None 17 | stop = stop - 1 18 | curr = self.head 19 | count = 1 20 | while curr is not None: 21 | if count == start: 22 | break 23 | curr = curr.next 24 | count += 1 25 | node = Node(curr.data) 26 | front = node 27 | back = front 28 | while count < stop: 29 | curr = curr.next 30 | node = Node(curr.data) 31 | back.next = node 32 | back = back.next 33 | count += 1 34 | 35 | return front 36 | 37 | def pop(self, i=-1): 38 | """ Remove the item at the given position in the linked list, 39 | and return it. 40 | If no index is specified, a.pop() removes and returns the last item 41 | in the linked list.""" 42 | if self.isEmpty(): 43 | return None 44 | if i >= self.size(): 45 | return None 46 | if (i + self.N) < 0: 47 | return None 48 | if i < 0: 49 | ind = i + self.N 50 | else: 51 | ind = i 52 | curr = self.head 53 | prev = None 54 | count = 0 55 | found = False 56 | while curr is not None: 57 | if count == ind: 58 | found = True 59 | break 60 | prev = curr 61 | curr = curr.next 62 | count += 1 63 | if found: 64 | node = curr.data 65 | if prev is None: 66 | self.head = self.head.next 67 | else: 68 | prev.next = curr.next 69 | curr.next = None 70 | self.N -= 1 71 | return node 72 | return None 73 | 74 | def insert(self, data, index): 75 | """ insert data at given index in the linked list""" 76 | if not isinstance(index, int): 77 | print ("Index should be of type Integer, str() passed") 78 | return False 79 | if index < 0 or index > self.size(): 80 | print ("Bad Index Range!") 81 | return False 82 | curr = self.head 83 | prev = None 84 | count = 0 85 | node = Node(data) 86 | 87 | if index == self.size(): 88 | print ("Inserting at tail") 89 | self.tail.next = node 90 | self.tail = self.tail.next 91 | self.N += 1 92 | return True 93 | 94 | while curr is not None: 95 | if count == index: 96 | break 97 | prev = curr 98 | curr = curr.next 99 | count += 1 100 | if prev is None: 101 | print ("Inserting at head") 102 | node.next = self.head 103 | self.head = node 104 | else: 105 | print ("Inserting in between: ", count) 106 | node.next = curr 107 | prev.next = node 108 | self.N += 1 109 | return True 110 | 111 | def index(self, x): 112 | """ return the index in the linked list of the first item 113 | whose value is x. 114 | Returns "None" if no such item in linked list """ 115 | curr = self.head 116 | index = 0 117 | while curr is not None: 118 | if curr.data == x: 119 | return index 120 | curr = curr.next 121 | index += 1 122 | return None 123 | 124 | def append(self, data): 125 | """ add the data at the end of the linked list """ 126 | node = Node(data) 127 | # If first node in the list then both head n tail should point to it 128 | if self.isEmpty(): 129 | self.head = node 130 | self.tail = self.head 131 | else: 132 | self.tail.next = node 133 | self.tail = self.tail.next 134 | self.N += 1 135 | 136 | def search(self, key): 137 | """search for key and if found return the index. return -1 otherwise""" 138 | curr = self.head 139 | index = 0 140 | found = False 141 | while curr is not None: 142 | if key == curr.data: 143 | found = True 144 | return index 145 | curr = curr.next 146 | index += 1 147 | if found: 148 | return index 149 | return -1 150 | 151 | def size(self): 152 | return self.N 153 | 154 | def isEmpty(self): 155 | return self.N == 0 156 | 157 | def __str__(self): 158 | sll = [] 159 | curr = self.head 160 | while curr is not None: 161 | sll.append(curr.data) 162 | curr = curr.next 163 | print (sll) 164 | -------------------------------------------------------------------------------- /chap3-BasicDataStructure/SLListClient.py: -------------------------------------------------------------------------------- 1 | from SLList import LinkedList 2 | 3 | 4 | def testLinkedList(): 5 | ll = LinkedList() 6 | while True: 7 | print ("Choose operation: ") 8 | print (" 1 - Append\n", 9 | "2 - Index\n", 10 | "3 - Pop\n", 11 | "4 - Search\n", 12 | "5 - Insert\n", 13 | "6 - Size\n", 14 | "7 - Slice\n", 15 | "8 - Exit\n") 16 | choice = input() 17 | if choice == '1': 18 | ll = addll(ll) 19 | ll.__str__() 20 | elif choice == '2': 21 | index(ll) 22 | ll.__str__() 23 | elif choice == '3': 24 | ll = pop(ll) 25 | ll.__str__() 26 | elif choice == '4': 27 | searchKey(ll) 28 | ll.__str__() 29 | elif choice == '5': 30 | insert(ll) 31 | ll.__str__() 32 | elif choice == '6': 33 | size(ll) 34 | ll.__str__() 35 | elif choice == '7': 36 | sliceList(ll) 37 | elif choice == '8': 38 | break 39 | else: 40 | print ("BAD Choice! Choose from 1 to 8 numbers") 41 | 42 | 43 | def sliceList(ll): 44 | start = input("Enter start index: ") 45 | end = input("Enter stop index: ") 46 | start = start.strip() 47 | end = end.strip() 48 | if not start.isdigit() or not end.isdigit(): 49 | print ("Bad start or stop index") 50 | return None 51 | start = int(start) 52 | end = int(end) 53 | front = ll.slice(start, end) 54 | curr = front 55 | result = [] 56 | while curr is not None: 57 | result.append(curr.data) 58 | curr = curr.next 59 | print ("Sliced list: ") 60 | print (result) 61 | 62 | 63 | def insert(ll): 64 | print ("Enter data to be inserted: ") 65 | data = input() 66 | print ("Index at which data to be inserted: ") 67 | index = input() 68 | index = index.strip() 69 | if not index.isdigit(): 70 | print ("Bad Index!") 71 | index = int(index) 72 | if index < 0 or index > ll.size(): 73 | print ("Index out of range!") 74 | ll.insert(data, index) 75 | return ll 76 | 77 | 78 | def pop(ll): 79 | print ("Enter index to be popped: ") 80 | index = input() 81 | index = index.strip() 82 | try: 83 | index = int(index) 84 | except: 85 | print ("Bad Input for Index") 86 | return ll 87 | data = ll.pop(index) 88 | if data is None: 89 | print ("No data found for index: ", index) 90 | else: 91 | print("Popped: ", data) 92 | return ll 93 | 94 | 95 | def index(ll): 96 | print ("Enter data for which index needs to be found: ") 97 | data = input() 98 | ind = ll.index(data) 99 | if ind is not None: 100 | print ("Index is: ", ind) 101 | else: 102 | print ("Data NOT FOUND!") 103 | 104 | 105 | def addll(ll): 106 | print ("Enter data: ") 107 | data = input() 108 | ll.append(data) 109 | return ll 110 | 111 | 112 | def searchKey(ll): 113 | print ("Enter data to be searched: ") 114 | data = input() 115 | result = ll.search(data) 116 | if result >= 0: 117 | print ("FOUND at index: ", result) 118 | else: 119 | print ("Not Found!") 120 | 121 | 122 | def size(ll): 123 | print("Size: ", ll.size()) 124 | 125 | 126 | def main(): 127 | testLinkedList() 128 | 129 | if __name__ == '__main__': 130 | main() 131 | -------------------------------------------------------------------------------- /chap3-BasicDataStructure/Stack.py: -------------------------------------------------------------------------------- 1 | # implementation of stack ADT 2 | 3 | 4 | class Stack: 5 | 6 | def __init__(self): 7 | self.items = [] 8 | 9 | def is_empty(self): 10 | return self.items == [] 11 | 12 | def push(self, value): 13 | self.items.append(value) 14 | 15 | def pop(self): 16 | return self.items.pop() 17 | 18 | def peek(self): 19 | return self.items[len(self.items)-1] 20 | 21 | def size(self): 22 | return len(self.items) 23 | -------------------------------------------------------------------------------- /chap3-BasicDataStructure/StackClient.py: -------------------------------------------------------------------------------- 1 | from Stack import Stack 2 | 3 | 4 | def testStackAPIs(): 5 | stack = Stack() 6 | print ("stack is empty? {}".format(stack.is_empty())) 7 | stack.push(4) 8 | stack.push('dog') 9 | print (stack.peek()) 10 | stack.push(True) 11 | print ("stack is empty? {}".format(stack.is_empty())) 12 | stack.push(8.4) 13 | print (stack.pop()) 14 | print (stack.pop()) 15 | print (stack.size()) 16 | 17 | 18 | def main(): 19 | testStackAPIs() 20 | 21 | 22 | if __name__ == '__main__': 23 | main() 24 | -------------------------------------------------------------------------------- /chap3-BasicDataStructure/StackLinkedList.py: -------------------------------------------------------------------------------- 1 | class Node: 2 | 3 | def __init__(self, data): 4 | self.data = data 5 | self.next = None 6 | 7 | 8 | class Stack: 9 | 10 | def __init__(self): 11 | self.head = None 12 | self.N = 0 13 | 14 | def __str__(self): 15 | dataList = [] 16 | curr = self.head 17 | while curr is not None: 18 | dataList.append(curr.data) 19 | curr = curr.next 20 | print (dataList) 21 | 22 | def pop(self): 23 | if self.isEmpty(): 24 | print ("Stack is EMPTY!") 25 | return None 26 | node = self.head 27 | self.head = self.head.next 28 | node.next = None 29 | self.N -= 1 30 | return node 31 | 32 | def push(self, data): 33 | node = Node(data) 34 | node.next = self.head 35 | self.head = node 36 | self.N += 1 37 | 38 | def isEmpty(self): 39 | return self.N == 0 40 | 41 | def size(self): 42 | return self.N 43 | -------------------------------------------------------------------------------- /chap3-BasicDataStructure/StackLinkedListClient.py: -------------------------------------------------------------------------------- 1 | from StackLinkedList import Stack 2 | 3 | 4 | def testStackAPIs(): 5 | stack = Stack() 6 | while True: 7 | print ("Choose Operation\n", 8 | "1 - Push(data)\n", 9 | "2 - Pop()\n", 10 | "3 - Size()\n", 11 | "4 - Exit()") 12 | choice = input("Choice: ") 13 | if choice == '1': 14 | stack = pushStack(stack) 15 | stack.__str__() 16 | elif choice == '2': 17 | stack = popStack(stack) 18 | stack.__str__() 19 | elif choice == '3': 20 | stackSize(stack) 21 | stack.__str__() 22 | elif choice == '4': 23 | break 24 | else: 25 | print ("Bad Choice. Please choose a valid operation") 26 | continue 27 | 28 | 29 | def stackSize(stack): 30 | size = stack.size() 31 | print("Stack Size: ", size) 32 | 33 | 34 | def pushStack(stack): 35 | data = input("Enter data: ") 36 | stack.push(data) 37 | return stack 38 | 39 | 40 | def popStack(stack): 41 | node = stack.pop() 42 | if node is not None: 43 | print ("Popped: ", node.data) 44 | else: 45 | print ("Popped: ", node) 46 | return stack 47 | 48 | 49 | def main(): 50 | testStackAPIs() 51 | 52 | if __name__ == '__main__': 53 | main() 54 | -------------------------------------------------------------------------------- /chap3-BasicDataStructure/Tokenizer.py: -------------------------------------------------------------------------------- 1 | def tokenizer(expr): 2 | if expr is None: 3 | return None 4 | expr = expr.strip() 5 | tokenList = [] 6 | accum = [] 7 | for t in expr: 8 | if t.isdigit(): 9 | accum.append(t) 10 | continue 11 | elif len(accum) > 0: 12 | tokenList.append(''.join(accum)) 13 | accum = [] 14 | if t == '+' or t == '-' or t == '/' or t == '*' or t == '^': 15 | tokenList.append(t) 16 | elif t == '(' or t == ')': 17 | tokenList.append(t) 18 | if len(accum) > 0: 19 | tokenList.append(''.join(accum)) 20 | return tokenList 21 | 22 | 23 | def readInput(): 24 | print ("Enter expression for calculator: ") 25 | expr = input() 26 | return expr 27 | 28 | 29 | def main(): 30 | expr = readInput() 31 | tknList = tokenizer(expr) 32 | print ("TokenList: ", tknList) 33 | 34 | 35 | if __name__ == '__main__': 36 | main() 37 | -------------------------------------------------------------------------------- /chap3-BasicDataStructure/UnorderedList.py: -------------------------------------------------------------------------------- 1 | from Node import Node 2 | 3 | 4 | class UnorderedList: 5 | 6 | def __init__(self): 7 | self.head = None 8 | 9 | def isEmpty(self): 10 | return self.head is None 11 | 12 | def add(self, data): 13 | temp = Node(data) 14 | temp.setNext(self.head) 15 | self.head = temp 16 | 17 | def size(self): 18 | current = self.head 19 | count = 0 20 | while not (current is None): 21 | count += 1 22 | current = current.getNext() 23 | return count 24 | 25 | def search(self, data): 26 | current = self.head 27 | while not (current is None): 28 | if current.getData() == data: 29 | return True 30 | current = current.getNext() 31 | return False 32 | 33 | def remove(self, data): 34 | current = self.head 35 | prev = None 36 | found = False 37 | 38 | while not found: 39 | if current.getData() == data: 40 | found = True 41 | else: 42 | prev = current 43 | current = current.getNext() 44 | if prev is None: 45 | self.head = current.getNext() 46 | else: 47 | prev.setNext(current.getNext()) 48 | -------------------------------------------------------------------------------- /chap3-BasicDataStructure/printingtask/Printer.py: -------------------------------------------------------------------------------- 1 | class Printer: 2 | 3 | def __init__(self, ppm): 4 | self.pageRate = ppm 5 | self.currentTask = None 6 | self.timeRemaining = 0 7 | 8 | def tick(self): 9 | if self.currentTask != None: 10 | self.timeRemaining = self.timeRemaining - 1 11 | if self.timeRemaining <= 0: 12 | self.currentTask = None 13 | 14 | def busy(self): 15 | if self.currentTask != None: 16 | return True 17 | else: 18 | return False 19 | 20 | def startNext(self, newTask): 21 | self.currentTask = newTask 22 | self.timeRemaining = (newTask.getPages() * 60) / self.pageRate 23 | 24 | -------------------------------------------------------------------------------- /chap3-BasicDataStructure/printingtask/Queue.py: -------------------------------------------------------------------------------- 1 | # Implementation of Queue ADT 2 | 3 | 4 | class Queue: 5 | 6 | def __init__(self): 7 | self.items = [] 8 | 9 | def is_empty(self): 10 | return self.items == [] 11 | 12 | def enqueue(self, value): 13 | self.items.insert(0, value) 14 | 15 | def dequeue(self): 16 | return self.items.pop() 17 | 18 | def size(self): 19 | return len(self.items) 20 | -------------------------------------------------------------------------------- /chap3-BasicDataStructure/printingtask/Simulation.py: -------------------------------------------------------------------------------- 1 | import random 2 | from Printer import Printer 3 | from Task import Task 4 | from Queue import Queue 5 | 6 | 7 | def simulation(numSeconds, pagePerMin): 8 | labPrinter = Printer(pagePerMin) 9 | printQ = Queue() 10 | waitTime = [] 11 | 12 | for currentSecond in range(numSeconds): 13 | if newPrintTask(): 14 | task = Task(currentSecond) 15 | printQ.enqueue(task) 16 | 17 | if (not labPrinter.busy()) and (not printQ.is_empty()): 18 | nextTask = printQ.dequeue() 19 | waitTime.append(nextTask.waitTime(currentSecond)) 20 | labPrinter.startNext(nextTask) 21 | 22 | labPrinter.tick() 23 | avgWait = sum(waitTime)/len(waitTime) 24 | print ("Avg wait: %6.2f secs, Tasks remaining: %3d" 25 | % (avgWait, printQ.size())) 26 | 27 | 28 | def newPrintTask(): 29 | num = random.randrange(1, 181) 30 | if num == 180: 31 | return True 32 | else: 33 | return False 34 | 35 | 36 | def readInput(): 37 | while True: 38 | pagerate = input("Please enter page rate: ") 39 | if pagerate.isdigit(): 40 | return int(pagerate) 41 | else: 42 | print ("Bad Input! Try again..") 43 | 44 | 45 | def main(): 46 | pagerate = readInput() 47 | for i in range(10): 48 | # simulating for 1 hour with input pagerate 49 | simulation(3600, pagerate) 50 | 51 | if __name__ == '__main__': 52 | main() 53 | -------------------------------------------------------------------------------- /chap3-BasicDataStructure/printingtask/Simulation_inout.txt: -------------------------------------------------------------------------------- 1 | Please enter page rate: 10 2 | Avg wait: 36.64 secs, Tasks remaining: 0 3 | Avg wait: 29.00 secs, Tasks remaining: 2 4 | Avg wait: 13.79 secs, Tasks remaining: 0 5 | Avg wait: 19.70 secs, Tasks remaining: 0 6 | Avg wait: 21.52 secs, Tasks remaining: 0 7 | Avg wait: 6.00 secs, Tasks remaining: 0 8 | Avg wait: 7.25 secs, Tasks remaining: 0 9 | Avg wait: 4.85 secs, Tasks remaining: 0 10 | Avg wait: 21.38 secs, Tasks remaining: 0 11 | Avg wait: 15.80 secs, Tasks remaining: 0 12 | ============================================================== 13 | Please enter page rate: 5 14 | Avg wait: 250.47 secs, Tasks remaining: 0 15 | Avg wait: 461.14 secs, Tasks remaining: 9 16 | Avg wait: 0.00 secs, Tasks remaining: 0 17 | Avg wait: 46.82 secs, Tasks remaining: 0 18 | Avg wait: 9.78 secs, Tasks remaining: 0 19 | Avg wait: 25.36 secs, Tasks remaining: 0 20 | Avg wait: 60.06 secs, Tasks remaining: 2 21 | Avg wait: 20.42 secs, Tasks remaining: 1 22 | Avg wait: 205.17 secs, Tasks remaining: 4 23 | Avg wait: 45.38 secs, Tasks remaining: 0 24 | ============================================================== 25 | Please enter page rate: 3 26 | Avg wait: 440.25 secs, Tasks remaining: 3 27 | Avg wait: 576.60 secs, Tasks remaining: 4 28 | Avg wait: 331.86 secs, Tasks remaining: 3 29 | Avg wait: 244.43 secs, Tasks remaining: 0 30 | Avg wait: 732.33 secs, Tasks remaining: 5 31 | Avg wait: 146.88 secs, Tasks remaining: 2 32 | Avg wait: 489.07 secs, Tasks remaining: 8 33 | Avg wait: 602.88 secs, Tasks remaining: 3 34 | Avg wait: 223.53 secs, Tasks remaining: 4 35 | Avg wait: 375.79 secs, Tasks remaining: 2 36 | 37 | -------------------------------------------------------------------------------- /chap3-BasicDataStructure/printingtask/Task.py: -------------------------------------------------------------------------------- 1 | import random 2 | 3 | 4 | class Task: 5 | 6 | def __init__(self, time): 7 | self.timeStamp = time 8 | self.pages = random.randrange(1, 21) 9 | 10 | def getStamp(self): 11 | return self.timeStamp 12 | 13 | def getPages(self): 14 | return self.pages 15 | 16 | def waitTime(self, currentTime): 17 | return currentTime - self.timeStamp 18 | -------------------------------------------------------------------------------- /chap3-BasicDataStructure/rev_string.py: -------------------------------------------------------------------------------- 1 | from Stack import Stack 2 | 3 | 4 | def readInput(): 5 | st = input("Enter String: ") 6 | return st 7 | 8 | 9 | def reverseString(st): 10 | s = Stack() 11 | stLen = len(st) 12 | for i in range(stLen): 13 | s.push(st[i]) 14 | 15 | outStr = [] 16 | for i in range(stLen): 17 | outStr.append(s.pop()) 18 | 19 | rev = ''.join(outStr) 20 | return rev 21 | 22 | 23 | def main(): 24 | st = readInput() 25 | rev_st = reverseString(st) 26 | print ("reversed string: %s" % rev_st) 27 | 28 | 29 | if __name__ == '__main__': 30 | main() 31 | -------------------------------------------------------------------------------- /chap4-Recursion/Programming_Exercises_Chap4.md: -------------------------------------------------------------------------------- 1 | Chapter 4 - Programming Exercises 2 | =================================== 3 | 4 | #####1. Write a recursive function to compute the factorial of a number. 5 | > Please refer to factorial_rec.py for implementation 6 | 7 | #####2. Write a recursive function to reverse a list. 8 | > Refer to list_reverse.py for implementation details 9 | 10 | #####3. Write a recursive function to compute the Fibonacci sequence. How does the performance of the recursive function compare to that of an iterative version? 11 | > Please refer to fibonacci.py for iterative and recursive implementation. We have noted the timings as well. Recursive implementation has poor performance in terms of time because there are lot of overlapping problems and each problems gets computer irrespective of whether it has been computed in the past or not. While in the iterative implementation we solve for a specific fibonacci n only once. 12 | 13 | #####4. Write a program that prints out Pascal’s triangle. Your program should accept a parameter that tells how many rows of the triangle to print. 14 | > Please see the implementation in pascal_triangle.py file - try out yourself :) 15 | -------------------------------------------------------------------------------- /chap4-Recursion/Stack.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | class Stack: 4 | 5 | def __init__(self): 6 | self.stack = [] 7 | 8 | def push(self, value): 9 | self.stack.append(value) 10 | 11 | def pop(self): 12 | return self.stack.pop() 13 | 14 | def peek(self): 15 | if self.isEmpty(): 16 | return None 17 | return self.stack[self.size - 1] 18 | 19 | def isEmpty(self): 20 | return len(self.stack) == 0 21 | 22 | def size(self): 23 | return len(self.stack) 24 | -------------------------------------------------------------------------------- /chap4-Recursion/draw_spiral.py: -------------------------------------------------------------------------------- 1 | # Below is only compatible with python-2.7 2 | import turtle 3 | 4 | 5 | def drawSpiral(my_turtle, line_len): 6 | if line_len > 0: 7 | my_turtle.forward(line_len) 8 | my_turtle.right(90) 9 | drawSpiral(my_turtle, line_len - 5) 10 | 11 | 12 | def main(): 13 | my_turtle = turtle.Turtle() 14 | my_win = turtle.Screen() 15 | drawSpiral(my_turtle, 100) 16 | my_win.exitonclick() 17 | 18 | 19 | if __name__ == '__main__': 20 | main() 21 | -------------------------------------------------------------------------------- /chap4-Recursion/factorial_rec.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | def factorialRec(num): 4 | if num <= 1: 5 | return 1 6 | return num * factorialRec(num-1) 7 | 8 | 9 | def readInput(): 10 | while True: 11 | num = input("Enter number: ") 12 | num = num.strip() 13 | if num.isdigit(): 14 | num = int(num) 15 | return num 16 | else: 17 | print ("Bad Input. Try again..") 18 | 19 | 20 | def main(): 21 | num = readInput() 22 | fact = factorialRec(num) 23 | print ("Factorial of %d is: %d" % (num, fact)) 24 | 25 | 26 | if __name__ == '__main__': 27 | main() 28 | -------------------------------------------------------------------------------- /chap4-Recursion/fibonacci.py: -------------------------------------------------------------------------------- 1 | import time 2 | 3 | 4 | def readInput(): 5 | while True: 6 | num = input("which fibonacci number you want: ") 7 | num = num.strip() 8 | if not num.isdigit(): 9 | print ("Bad input, enter integer") 10 | continue 11 | num = int(num) 12 | if num < 0: 13 | print ("Bad Input, please enter positive integers") 14 | continue 15 | return num 16 | 17 | 18 | def fibonacci_iter(num): 19 | if num == 0 or num == 1: 20 | return num 21 | fib1 = 0 22 | fib2 = 1 23 | count = 2 24 | result = 1 25 | while count <= num: 26 | result = fib1 + fib2 27 | fib1, fib2 = fib2, result 28 | count += 1 29 | return result 30 | 31 | 32 | def fibonacci_rec(num): 33 | # if num == 0 or num == 1: 34 | if num <= 2: 35 | return 1 36 | return fibonacci_rec(num-1) + fibonacci_rec(num-2) 37 | 38 | 39 | def main(): 40 | num = readInput() 41 | # lets first calculate fibonacci iteratively 42 | start = time.clock() 43 | result = fibonacci_iter(num) 44 | end = time.clock() 45 | print("Fibonacci number %d is: %d in time(iterative): %f" % 46 | (num, result, round((end - start), 5))) 47 | # now calculate fibonacci recursively 48 | start = time.clock() 49 | result = fibonacci_rec(num) 50 | end = time.clock() 51 | print("Fibonacci number %d is: %d in time(recursively): %f" % 52 | (num, result, round((end - start), 5))) 53 | 54 | 55 | if __name__ == '__main__': 56 | main() 57 | -------------------------------------------------------------------------------- /chap4-Recursion/hanoi_tower.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | def readInput(): 4 | while True: 5 | disks = input("Enter number of disks: ") 6 | disks = disks.strip() 7 | if not disks.isdigit(): 8 | print ("Bad input! Try again..") 9 | continue 10 | disks = int(disks) 11 | return disks 12 | 13 | 14 | def towerOfHanoi(disks): 15 | moveTower(disks, "A", "B", "C") 16 | 17 | 18 | def moveTower(height, src, dest, via): 19 | if height >= 1: 20 | moveTower(height - 1, src, via, dest) 21 | moveDisk(src, dest) 22 | moveTower(height - 1, via, dest, src) 23 | 24 | 25 | def moveDisk(src, dest): 26 | print ("Moving disk from: %s to %s" % (src, dest)) 27 | 28 | 29 | def main(): 30 | disks = readInput() 31 | towerOfHanoi(disks) 32 | 33 | 34 | if __name__ == '__main__': 35 | main() 36 | -------------------------------------------------------------------------------- /chap4-Recursion/intstr_base.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | def readInput(): 4 | while True: 5 | num = input("Enter number: ") 6 | num = num.strip() 7 | if not num.isdigit(): 8 | print ("Bad Input. Try again..") 9 | continue 10 | num = int(num) 11 | base = input("Enter base : ") 12 | if not base.isdigit() or int(base.strip()) > 16: 13 | print ("Bad base input. Plz try again..") 14 | continue 15 | return (num, int(base.strip())) 16 | 17 | 18 | def toString(num, base): 19 | convertStr = "0123456789ABCDEF" 20 | if num < base: 21 | return convertStr[num] 22 | else: 23 | return toString(num // base, base) + convertStr[num % base] 24 | 25 | 26 | def main(): 27 | num, base = readInput() 28 | ans = toString(num, base) 29 | print ("number:%d in base:%d is: %s" % (num, base, ans)) 30 | 31 | 32 | if __name__ == '__main__': 33 | main() 34 | -------------------------------------------------------------------------------- /chap4-Recursion/intstrbase_stack.py: -------------------------------------------------------------------------------- 1 | from Stack import Stack 2 | 3 | 4 | def readInput(): 5 | while True: 6 | num = input("Enter number: ") 7 | base = input("Enter base: ") 8 | num = num.strip() 9 | base = base.strip() 10 | if not num.isdigit() or not base.isdigit(): 11 | print ("Bad input. Try again...") 12 | continue 13 | num = int(num) 14 | base = int(base) 15 | if base < 2 or base > 16: 16 | print ("Bad base value.") 17 | continue 18 | return num, base 19 | 20 | 21 | def convertIntToStringBase(num, base): 22 | convString = "0123456789ABCDEF" 23 | stack = Stack() 24 | while num > 0: 25 | if num < base: 26 | stack.push(convString[num]) 27 | else: 28 | stack.push(convString[num % base]) 29 | num = num // base 30 | result = [] 31 | while not stack.isEmpty(): 32 | result.append(stack.pop()) 33 | return ''.join(result) 34 | 35 | 36 | def main(): 37 | num, base = readInput() 38 | result = convertIntToStringBase(num, base) 39 | print ("Number: %d in Base: %d is: %s" % (num, base, result)) 40 | 41 | 42 | if __name__ == '__main__': 43 | main() 44 | -------------------------------------------------------------------------------- /chap4-Recursion/list_reverse.py: -------------------------------------------------------------------------------- 1 | # python-3 2 | def readInput(): 3 | print ("Enter space separated list of data elements:") 4 | data = input() 5 | data = data.strip() 6 | data = data.split(" ") 7 | return data 8 | 9 | 10 | def reverseList(nlist): 11 | if len(nlist) == 1: 12 | return nlist 13 | revList = reverseList(nlist[1:]) 14 | revList.append(nlist[0]) 15 | return revList 16 | 17 | 18 | def main(): 19 | nlist = readInput() 20 | result = reverseList(nlist) 21 | print ("Reversed list: ", result) 22 | 23 | 24 | if __name__ == '__main__': 25 | main() 26 | -------------------------------------------------------------------------------- /chap4-Recursion/listsum_recur.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | def findSum(numlist): 4 | if len(numlist) == 1: 5 | return numlist[0] 6 | return numlist[0] + findSum(numlist[1:]) 7 | 8 | 9 | def readInput(): 10 | while True: 11 | print("Enter list of numbers comma spearated: ") 12 | numlist = input() 13 | numlist = numlist.strip() 14 | numlist = numlist.split(",") 15 | try: 16 | numlist = [int(n.strip()) for n in numlist] 17 | return numlist 18 | except: 19 | print ("Bad Input. Try Again..") 20 | continue 21 | 22 | 23 | def main(): 24 | numlist = readInput() 25 | ans = findSum(numlist) 26 | print ("Sum is: ", ans) 27 | 28 | 29 | if __name__ == '__main__': 30 | main() 31 | -------------------------------------------------------------------------------- /chap4-Recursion/pascal_triangle.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | def readInput(): 4 | while True: 5 | row = input("Number of rows in pascal triangle: ") 6 | if isinstance(row, int): 7 | return row 8 | if not row.isdigit(): 9 | print ("Bad Input. Try again..") 10 | continue 11 | row = int(row) 12 | if row < 1: 13 | print ("Bad Input, please enter positive integer") 14 | continue 15 | return row 16 | 17 | 18 | def printPascalTriangle(nrow): 19 | printRow([1], nrow) 20 | pascalTriangle(nrow-1, [1]) 21 | 22 | 23 | def pascalTriangle(nrow, rlist): 24 | if nrow == 0: 25 | return 26 | tmp_row = [] 27 | tmp_row.append(rlist[0]) 28 | for i in range(0, len(rlist)-1): 29 | # print (rlist) 30 | tmp_row.append(rlist[i] + rlist[i+1]) 31 | tmp_row.append(rlist[-1]) 32 | printRow(tmp_row, nrow) 33 | pascalTriangle(nrow-1, tmp_row) 34 | 35 | 36 | def printRow(rowData, numRow): 37 | prefix = " " * numRow 38 | print (prefix), 39 | for data in rowData: 40 | print ("%d " % data), 41 | else: 42 | print 43 | 44 | 45 | def main(): 46 | nrow = readInput() 47 | printPascalTriangle(nrow) 48 | 49 | 50 | if __name__ == '__main__': 51 | main() 52 | -------------------------------------------------------------------------------- /chap4-Recursion/string_reverse.py: -------------------------------------------------------------------------------- 1 | def readInput(): 2 | string = input("Enter string: ") 3 | string = string.strip() 4 | return string 5 | 6 | 7 | def reverseString(string): 8 | if len(string) == 1: 9 | return string 10 | return reverseString(string[1:]) + string[0] 11 | 12 | 13 | def main(): 14 | string = readInput() 15 | revstr = reverseString(string) 16 | print ("Reversed String: %s" % revstr) 17 | 18 | 19 | if __name__ == '__main__': 20 | main() 21 | -------------------------------------------------------------------------------- /chap5-SortingSearching/HashTable.py: -------------------------------------------------------------------------------- 1 | from hash import hash 2 | 3 | 4 | class HashTable: 5 | 6 | def __init__(self, size=11): 7 | self.size = size 8 | self.slots = [None] * self.size 9 | self.data = [None] * self.size 10 | 11 | def __setitem__(self, key, data): 12 | self.put(key, data) 13 | 14 | def __getitem__(self, key): 15 | return self.get(key) 16 | 17 | def printHashTable(self): 18 | for index in range(len(self.slots)): 19 | key = self.slots[index] 20 | value = self.data[index] 21 | print ("%d: %s:%s" % (index, key, value)) 22 | 23 | def showSlots(self): 24 | print (self.slots) 25 | 26 | def showData(self): 27 | print (self.data) 28 | 29 | def hash_function(self, key, size): 30 | return hash(key, size) 31 | 32 | def rehash(self, old_hash, size): 33 | return (old_hash + 1) % size 34 | 35 | def get(self, key): 36 | start_slot = self.hash_function(key, len(self.slots)) 37 | 38 | data = None 39 | index = start_slot 40 | while self.slots[index] is not None: 41 | if self.slots[index] == key: 42 | data = self.data[index] 43 | break 44 | else: 45 | index = self.rehash(index, len(self.slots)) 46 | if index == start_slot: 47 | break 48 | return data 49 | 50 | def put(self, key, data): 51 | hashVal = self.hash_function(key, len(self.slots)) 52 | 53 | if self.slots[hashVal] is None: 54 | self.slots[hashVal] = key 55 | self.data[hashVal] = data 56 | elif self.slots[hashVal] == key: 57 | self.data[hashVal] = data # replace 58 | else: 59 | next_slot = self.rehash(hashVal, len(self.slots)) 60 | while self.slots[next_slot] is not None and \ 61 | self.slots[next_slot] != key: 62 | next_slot = self.rehash(next_slot, len(self.slots)) 63 | 64 | if self.slots[next_slot] is None: 65 | self.slots[next_slot] = key 66 | self.data[next_slot] = data 67 | else: 68 | self.data[next_slot] = data # replace 69 | -------------------------------------------------------------------------------- /chap5-SortingSearching/HashTableClient.py: -------------------------------------------------------------------------------- 1 | from HashTable import HashTable 2 | 3 | 4 | def testHashTable(): 5 | ht = HashTable() 6 | while True: 7 | print ("choose Operation: \n", 8 | "1 - Set Item\n", 9 | "2 - Get Item\n", 10 | "3 - GetKeys\n", 11 | "4 - Exit") 12 | choice = input("Enter choice: ") 13 | if choice == '1': 14 | ht = setItem(ht) 15 | ht.printHashTable() 16 | elif choice == '2': 17 | getItem(ht) 18 | ht.printHashTable() 19 | elif choice == '3': 20 | ht.showSlots() 21 | elif choice == '4': 22 | break 23 | else: 24 | print ("Bad Choice - Choose Valid Operation") 25 | continue 26 | 27 | 28 | def getItem(ht): 29 | key = input("Enter key: ") 30 | value = ht.get(key) 31 | if value: 32 | print ("%s: %s" % (key, value)) 33 | else: 34 | print ("No value found for key: %s" % key) 35 | 36 | 37 | def setItem(ht): 38 | key = input("Enter key: ") 39 | data = input("Enter data: ") 40 | ht.put(key, data) 41 | return ht 42 | 43 | 44 | def main(): 45 | testHashTable() 46 | 47 | if __name__ == '__main__': 48 | main() 49 | -------------------------------------------------------------------------------- /chap5-SortingSearching/Numbers.py: -------------------------------------------------------------------------------- 1 | import random 2 | 3 | 4 | class Numbers: 5 | 6 | def __init__(self): 7 | self.num_list = [] 8 | 9 | def getNumbers(self, minRange, maxRange, nums): 10 | if not isinstance(nums, int): 11 | print ("ERROR: Bad input - \"nums\"") 12 | elif not isinstance(minRange, int): 13 | print ("ERROR: Bad input - \"minRange\"") 14 | elif not isinstance(maxRange, int): 15 | print ("ERROR: Bad input - \"maxRange\"") 16 | elif minRange > maxRange: 17 | print ("ERROR: expected minRange < maxRange") 18 | 19 | else: 20 | for i in range(nums): 21 | data = random.randint(minRange, maxRange) 22 | self.num_list.append(data) 23 | return self.num_list 24 | -------------------------------------------------------------------------------- /chap5-SortingSearching/binary_search.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | def readInput(): 4 | print ("Enter space separated data: ") 5 | nlist = input() 6 | nlist = nlist.strip() 7 | nums = nlist.split(" ") 8 | nlist = [int(n) for n in nums] 9 | return nlist 10 | 11 | 12 | def readSearchKey(): 13 | while True: 14 | key = input("Enter search key: ") 15 | if isinstance(key, int): 16 | return key 17 | if not key.isdigit(): 18 | continue 19 | key = int(key) 20 | return key 21 | 22 | 23 | def binarySearch(numList, key): 24 | if numList is None or len(numList) < 1: 25 | return False 26 | low = 0 27 | high = len(numList) - 1 28 | while low <= high: 29 | mid = low + ((high - low) // 2) 30 | if numList[mid] == key: 31 | return True 32 | elif key < numList[mid]: 33 | high = mid - 1 34 | else: 35 | low = mid + 1 36 | return False 37 | 38 | 39 | def main(): 40 | numList = readInput() 41 | numListSorted = sorted(numList) 42 | while True: 43 | key = readSearchKey() 44 | result = binarySearch(numListSorted, key) 45 | print ("Key: %d in the list: %s" % (key, result)) 46 | print (numListSorted) 47 | 48 | if __name__ == '__main__': 49 | main() 50 | -------------------------------------------------------------------------------- /chap5-SortingSearching/bubble_sort.py: -------------------------------------------------------------------------------- 1 | from Numbers import Numbers 2 | 3 | 4 | def readParams(): 5 | while True: 6 | minRange = input("Enter min range: ") 7 | maxRange = input("Enter max range: ") 8 | nums = input("Enter total numbers to be generated: ") 9 | if isinstance(minRange, int) and isinstance(maxRange, int) \ 10 | and isinstance(nums, int): 11 | return minRange, maxRange, nums 12 | elif minRange.isdigit() and maxRange.isdigit() and nums.isdigit(): 13 | minRange = int(minRange) 14 | maxRange = int(maxRange) 15 | nums = int(nums) 16 | return minRange, maxRange, nums 17 | else: 18 | print ("Bad Input. Please try again..") 19 | 20 | 21 | def bubbleSort(A): 22 | for index in range(len(A)): 23 | for i in range(index+1): 24 | if A[i] > A[index]: 25 | A[i], A[index] = A[index], A[i] 26 | return A 27 | 28 | 29 | def main(): 30 | minRange, maxRange, nums = readParams() 31 | nm = Numbers() 32 | numbers = nm.getNumbers(minRange, maxRange, nums) 33 | print ("Random generated %d numbers between %d and %d are: " % 34 | (nums, minRange, maxRange)) 35 | print (numbers) 36 | sortedNums = bubbleSort(numbers) 37 | print ("Sorted numbers are: ") 38 | print (sortedNums) 39 | 40 | 41 | if __name__ == '__main__': 42 | main() 43 | -------------------------------------------------------------------------------- /chap5-SortingSearching/bubble_sort_fast.py: -------------------------------------------------------------------------------- 1 | from Numbers import Numbers 2 | 3 | 4 | def readParams(): 5 | while True: 6 | minRange = input("Enter min range: ") 7 | maxRange = input("Enter max range: ") 8 | nums = input("Enter total numbers to be generated: ") 9 | if isinstance(minRange, int) and isinstance(maxRange, int) \ 10 | and isinstance(nums, int): 11 | return minRange, maxRange, nums 12 | elif minRange.isdigit() and maxRange.isdigit() and nums.isdigit(): 13 | minRange = int(minRange) 14 | maxRange = int(maxRange) 15 | nums = int(nums) 16 | return minRange, maxRange, nums 17 | else: 18 | print ("Bad Input. Please try again..") 19 | 20 | 21 | def bubbleSort(A): 22 | exchange = True 23 | numPass = len(A) - 1 24 | while numPass > 0 and exchange: 25 | exchange = False 26 | for i in range(numPass): 27 | if A[i] > A[i + 1]: 28 | A[i], A[i + 1] = A[i + 1], A[i] 29 | exchange = True 30 | numPass -= 1 31 | return A 32 | 33 | 34 | def main(): 35 | minRange, maxRange, nums = readParams() 36 | nm = Numbers() 37 | numbers = nm.getNumbers(minRange, maxRange, nums) 38 | print ("Random generated %d numbers between %d and %d are: " % 39 | (nums, minRange, maxRange)) 40 | print (numbers) 41 | sortedNums = bubbleSort(numbers) 42 | print ("Sorted numbers are: ") 43 | print (sortedNums) 44 | 45 | 46 | if __name__ == '__main__': 47 | main() 48 | -------------------------------------------------------------------------------- /chap5-SortingSearching/hash.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | def readTableSize(): 4 | while True: 5 | tsize = input("Enter table size: ") 6 | if isinstance(tsize, int): 7 | return tsize 8 | tsize = tsize.strip() 9 | if not tsize.isdigit(): 10 | print ("Bad table size - please input integer values") 11 | continue 12 | tsize = int(tsize) 13 | return tsize 14 | 15 | 16 | def readString(): 17 | string = input("Enter string: ") 18 | string = string.strip() 19 | return string 20 | 21 | 22 | def hash(string, tableSize): 23 | totalSum = 0 24 | for index in range(len(string)): 25 | totalSum += ord(string[index]) 26 | # print ("Total Ordinal Sum: ", totalSum) 27 | return totalSum % tableSize 28 | 29 | 30 | def hashWeighted(string, tableSize): 31 | totalSum = 0 32 | for index in range(len(string)): 33 | totalSum += ord(string[index])*(index+1) 34 | return totalSum % tableSize 35 | 36 | 37 | def main(): 38 | string = readString() 39 | tableSize = readTableSize() 40 | hashVal = hash(string, tableSize) 41 | wtdHashVal = hashWeighted(string, tableSize) 42 | print ("Hash Value: %s" % hashVal) 43 | print ("Hash Weighted Value: %s" % wtdHashVal) 44 | 45 | 46 | if __name__ == '__main__': 47 | main() 48 | -------------------------------------------------------------------------------- /chap5-SortingSearching/insertion_sort.py: -------------------------------------------------------------------------------- 1 | from Numbers import Numbers 2 | 3 | 4 | def readParams(): 5 | while True: 6 | minRange = input("Enter min range: ") 7 | maxRange = input("Enter max range: ") 8 | nums = input("Enter total numbers to be generated: ") 9 | if isinstance(minRange, int) and isinstance(maxRange, int) \ 10 | and isinstance(nums, int): 11 | return minRange, maxRange, nums 12 | elif minRange.isdigit() and maxRange.isdigit() and nums.isdigit(): 13 | minRange = int(minRange) 14 | maxRange = int(maxRange) 15 | nums = int(nums) 16 | return minRange, maxRange, nums 17 | else: 18 | print ("Bad Input. Please try again..") 19 | 20 | 21 | def insertionSort(A): 22 | for j in range(1, len(A)): 23 | key = A[j] 24 | i = j - 1 25 | while i >= 0 and A[i] > key: 26 | A[i+1] = A[i] 27 | i -= 1 28 | A[i+1] = key 29 | return A 30 | 31 | 32 | def main(): 33 | minRange, maxRange, nums = readParams() 34 | nm = Numbers() 35 | numbers = nm.getNumbers(minRange, maxRange, nums) 36 | print ("Random generated %d numbers between %d and %d are: " % 37 | (nums, minRange, maxRange)) 38 | print (numbers) 39 | sortedNums = insertionSort(numbers) 40 | print ("Sorted numbers are: ") 41 | print (sortedNums) 42 | 43 | 44 | if __name__ == '__main__': 45 | main() 46 | -------------------------------------------------------------------------------- /chap5-SortingSearching/merge_sort.py: -------------------------------------------------------------------------------- 1 | from Numbers import Numbers 2 | 3 | 4 | def readParams(): 5 | while True: 6 | minRange = input("Enter min range: ") 7 | maxRange = input("Enter max range: ") 8 | nums = input("Enter total numbers to be generated: ") 9 | if isinstance(minRange, int) and isinstance(maxRange, int) \ 10 | and isinstance(nums, int): 11 | return minRange, maxRange, nums 12 | elif minRange.isdigit() and maxRange.isdigit() and nums.isdigit(): 13 | minRange = int(minRange) 14 | maxRange = int(maxRange) 15 | nums = int(nums) 16 | return minRange, maxRange, nums 17 | else: 18 | print ("Bad Input. Please try again..") 19 | 20 | 21 | def merge(left, right): 22 | result = [] 23 | left_idx, right_idx = 0, 0 24 | 25 | while left_idx < len(left) and right_idx < len(right): 26 | if left[left_idx] <= right[right_idx]: 27 | result.append(left[left_idx]) 28 | left_idx += 1 29 | else: 30 | result.append(right[right_idx]) 31 | right_idx += 1 32 | 33 | if left: 34 | result.extend(left[left_idx:]) 35 | if right: 36 | result.extend(right[right_idx:]) 37 | return result 38 | 39 | 40 | def merge_sort(A): 41 | if len(A) <= 1: 42 | return A 43 | mid = len(A) // 2 44 | left = merge_sort(A[:mid]) 45 | right = merge_sort(A[mid:]) 46 | result = list(merge(left, right)) 47 | return result 48 | 49 | 50 | def main(): 51 | minRange, maxRange, nums = readParams() 52 | nm = Numbers() 53 | numbers = nm.getNumbers(minRange, maxRange, nums) 54 | print ("Random generated %d numbers between %d and %d are: " % 55 | (nums, minRange, maxRange)) 56 | print (numbers) 57 | sortedNums = merge_sort(numbers) 58 | print ("Sorted numbers are: ") 59 | print (sortedNums) 60 | 61 | 62 | if __name__ == '__main__': 63 | main() 64 | -------------------------------------------------------------------------------- /chap5-SortingSearching/quick_sort.py: -------------------------------------------------------------------------------- 1 | from Numbers import Numbers 2 | import random 3 | 4 | 5 | def readParams(): 6 | while True: 7 | minRange = input("Enter min range: ") 8 | maxRange = input("Enter max range: ") 9 | nums = input("Enter total numbers to be generated: ") 10 | if isinstance(minRange, int) and isinstance(maxRange, int) \ 11 | and isinstance(nums, int): 12 | return minRange, maxRange, nums 13 | elif minRange.isdigit() and maxRange.isdigit() and nums.isdigit(): 14 | minRange = int(minRange) 15 | maxRange = int(maxRange) 16 | nums = int(nums) 17 | return minRange, maxRange, nums 18 | else: 19 | print ("Bad Input. Please try again..") 20 | 21 | 22 | def quickSort(A): 23 | if len(A) <= 1: 24 | return A 25 | less = [] 26 | more = [] 27 | pivot = random.choice(A) 28 | for a in A: 29 | if a < pivot: 30 | less.append(a) 31 | if a > pivot: 32 | more.append(a) 33 | less = quickSort(less) 34 | more = quickSort(more) 35 | return less + [pivot] * A.count(pivot) + more 36 | 37 | 38 | def main(): 39 | minRange, maxRange, nums = readParams() 40 | nm = Numbers() 41 | numbers = nm.getNumbers(minRange, maxRange, nums) 42 | print ("Random generated %d numbers between %d and %d are: " % 43 | (nums, minRange, maxRange)) 44 | print (numbers) 45 | sortedNums = quickSort(numbers) 46 | print ("Sorted numbers are: ") 47 | print (sortedNums) 48 | 49 | if __name__ == '__main__': 50 | main() 51 | -------------------------------------------------------------------------------- /chap5-SortingSearching/selection_sort.py: -------------------------------------------------------------------------------- 1 | from Numbers import Numbers 2 | 3 | 4 | def readParams(): 5 | while True: 6 | minRange = input("Enter min range: ") 7 | maxRange = input("Enter max range: ") 8 | nums = input("Enter total numbers to be generated: ") 9 | if isinstance(minRange, int) and isinstance(maxRange, int) \ 10 | and isinstance(nums, int): 11 | return minRange, maxRange, nums 12 | elif minRange.isdigit() and maxRange.isdigit() and nums.isdigit(): 13 | minRange = int(minRange) 14 | maxRange = int(maxRange) 15 | nums = int(nums) 16 | return minRange, maxRange, nums 17 | else: 18 | print ("Bad Input. Please try again..") 19 | 20 | 21 | def selectionSort(A): 22 | for i in range(len(A)-1): 23 | imin = i 24 | for j in range(i, len(A)): 25 | if A[j] < A[imin]: 26 | imin = j 27 | if imin != i: 28 | A[imin], A[i] = A[i], A[imin] 29 | 30 | return A 31 | 32 | 33 | def main(): 34 | minRange, maxRange, nums = readParams() 35 | nm = Numbers() 36 | numbers = nm.getNumbers(minRange, maxRange, nums) 37 | print ("Random generated %d numbers between %d and %d are: " % 38 | (nums, minRange, maxRange)) 39 | print (numbers) 40 | sortedNums = selectionSort(numbers) 41 | print ("Sorted numbers are: ") 42 | print (sortedNums) 43 | 44 | 45 | if __name__ == '__main__': 46 | main() 47 | -------------------------------------------------------------------------------- /chap5-SortingSearching/shell_sort.py: -------------------------------------------------------------------------------- 1 | from Numbers import Numbers 2 | 3 | 4 | def readParams(): 5 | while True: 6 | minRange = input("Enter min range: ") 7 | maxRange = input("Enter max range: ") 8 | nums = input("Enter total numbers to be generated: ") 9 | if isinstance(minRange, int) and isinstance(maxRange, int) \ 10 | and isinstance(nums, int): 11 | return minRange, maxRange, nums 12 | elif minRange.isdigit() and maxRange.isdigit() and nums.isdigit(): 13 | minRange = int(minRange) 14 | maxRange = int(maxRange) 15 | nums = int(nums) 16 | return minRange, maxRange, nums 17 | else: 18 | print ("Bad Input. Please try again..") 19 | 20 | 21 | 22 | def shell_sort(A): 23 | inc = len(A)//2 24 | 25 | while inc > 0: 26 | for i, item in enumerate(A): 27 | while i >= inc and A[i-inc] > item: 28 | A[i] = A[i-inc] 29 | i = i - inc 30 | A[i] = item 31 | inc = 1 if inc == 2 else int(inc * 5.0 / 11) 32 | return A 33 | 34 | 35 | def main(): 36 | minRange, maxRange, nums = readParams() 37 | nm = Numbers() 38 | numbers = nm.getNumbers(minRange, maxRange, nums) 39 | print ("Random generated %d numbers between %d and %d are: " % 40 | (nums, minRange, maxRange)) 41 | print (numbers) 42 | sortedNums = shell_sort(numbers) 43 | print ("Sorted numbers are: ") 44 | print (sortedNums) 45 | 46 | 47 | if __name__ == '__main__': 48 | main() 49 | -------------------------------------------------------------------------------- /chap6-BinaryTrees/BinaryTree.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | 4 | class Node: 5 | 6 | def __init__(self, data): 7 | self.data = data 8 | self.left = None 9 | self.right = None 10 | 11 | 12 | class BinaryTree: 13 | 14 | def __init__(self): 15 | self.root = None 16 | self.N = 0 17 | 18 | def getrootnode(self): 19 | return self.root 20 | 21 | def isbst2(self): 22 | """ Returns true if the given tree is a binary search tree 23 | (efficient version). """ 24 | def isbst_util(node, mini, maxi): 25 | if node is None: 26 | return True 27 | if node.data < mini or node.data > maxi: 28 | return False 29 | return ( isbst_util(node.left, mini, node.data) and \ 30 | isbst_util(node.right, node.data+1, maxi)) 31 | 32 | return isbst_util(self.root, -(sys.maxsize), sys.maxsize) 33 | 34 | 35 | def isbst1(self): 36 | def isbst1_n2(node): 37 | if node is None: 38 | return True 39 | if node.left is not None and self.minvalue(node.left) > node.data: 40 | return False 41 | if node.right is not None and \ 42 | self.maxvalue(node.right) <= node.data: 43 | return False 44 | if not isbst1_n2(node.left) or not isbst1_n2(node.right): 45 | return False 46 | return True 47 | return isbst1_n2(self.root) 48 | 49 | def counttrees(self, numkeys): 50 | """ For the key values 1...numKeys, how many structurally unique 51 | binary search trees are possible that store those keys. """ 52 | if numkeys <= 1: 53 | return 1 54 | 55 | total = 0 56 | for root in range(1, numkeys+1): 57 | left = self.counttrees(root - 1) 58 | right = self.counttrees(numkeys - root) 59 | total += left * right 60 | return total 61 | 62 | def sametree(self, bintree): 63 | """ Given two trees, return true if they are 64 | structurally identical. """ 65 | def sametreeBT(anode, bnode): 66 | # both empty -> return true 67 | if anode is None and bnode is None: 68 | return True 69 | # both non-empty compare them 70 | elif anode is not None and bnode is not None: 71 | return (anode.data == bnode.data and \ 72 | sametreeBT(anode.left, bnode.left) and \ 73 | sametreeBT(anode.right, bnode.right)) 74 | else: 75 | # one node None other not -> return false 76 | return False 77 | return sametreeBT(self.root, bintree.getrootnode()) 78 | 79 | def doubletree(self): 80 | """ For each node in a binary search tree, 81 | create a new duplicate node, and insert 82 | the duplicate as the left child of the original node. 83 | The resulting tree should still be a binary search tree.""" 84 | def doubletreeBT(node): 85 | if node is None: 86 | return 87 | doubletreeBT(node.left) 88 | doubletreeBT(node.right) 89 | # duplicate node to left 90 | oldleft = node.left 91 | node.left = Node(node.data) 92 | node.left.left = oldleft 93 | doubletreeBT(self.root) 94 | 95 | def mirror(self): 96 | """ Change a tree so that the roles of the 97 | left and right pointers are swapped at every node.""" 98 | def mirrorBT(node): 99 | if node is None: 100 | return 101 | mirrorBT(node.left) 102 | mirrorBT(node.right) 103 | # swap the pointers here 104 | temp = node.left 105 | node.left = node.right 106 | node.right = temp 107 | mirrorBT(self.root) 108 | 109 | def printpaths(self): 110 | """Given a binary tree, print out all of its root-to-leaf 111 | paths, one per line.""" 112 | def printpathsBT(node): 113 | path = [None] * 1000 114 | printpathsrecur(node, path, 0) 115 | 116 | def printpathsrecur(node, path, pathlen): 117 | if node is None: 118 | return 119 | 120 | path[pathlen] = node.data 121 | pathlen = pathlen + 1 122 | 123 | # if its a leaf then print the path which led to here 124 | if (node.left is None) and (node.right is None): 125 | printList(path, pathlen) 126 | else: 127 | printpathsrecur(node.left, path, pathlen) 128 | printpathsrecur(node.right, path, pathlen) 129 | 130 | def printList(path, pathlen): 131 | for p in range(pathlen): 132 | print (path[p], sep=" ", end= " ") 133 | print () 134 | printpathsBT(self.root) 135 | 136 | 137 | def haspathsum(self, targetsum): 138 | """ Given a tree and a sum, return true if there is a path from the root 139 | down to a leaf, such that adding up all the values along the path 140 | equals the given sum.""" 141 | def haspathsumBT(node, targetsum): 142 | if node is None: 143 | return targetsum == 0 144 | # lets check both subtress 145 | return haspathsumBT(node.left, targetsum - node.data) or \ 146 | haspathsumBT(node.right, targetsum - node.data) 147 | return haspathsumBT(self.root, targetsum) 148 | 149 | def insert(self, data): 150 | def insertBST(node, data): 151 | if node is None: 152 | return Node(data) 153 | if data <= node.data: 154 | node.left = insertBST(node.left, data) 155 | else: 156 | node.right = insertBST(node.right, data) 157 | return node 158 | 159 | self.root = insertBST(self.root, data) 160 | 161 | def minvalue(self, root=None): 162 | def minvalbst(node): 163 | if node is None: 164 | return None 165 | while node.left is not None: 166 | node = node.left 167 | return node.data 168 | 169 | if root is None: 170 | root = self.root 171 | return minvalbst(root) 172 | 173 | def maxvalue(self, root=None): 174 | def maxvalbst(node): 175 | if node is None: 176 | return None 177 | while node.right is not None: 178 | node = node.right 179 | return node.data 180 | 181 | if root is None: 182 | root = self.root 183 | return maxvalbst(root) 184 | 185 | def maxdepth(self): 186 | def maxdepthBT(node): 187 | if node is None: 188 | return 0 189 | left = maxdepthBT(node.left) 190 | right = maxdepthBT(node.right) 191 | if left > right: 192 | return left + 1 193 | else: 194 | return right + 1 195 | return maxdepthBT(self.root) 196 | 197 | def size(self): 198 | def sizebt(node): 199 | if node is None: 200 | return 0 201 | return (sizebt(node.left) + 1 + sizebt(node.right)) 202 | return sizebt(self.root) 203 | 204 | def preorder(self): 205 | def preorderBT(node): 206 | if node is None: 207 | return 208 | print (node.data, sep=" ", end=" ") 209 | preorderBT(node.left) 210 | preorderBT(node.right) 211 | preorderBT(self.root) 212 | print () 213 | 214 | def postorder(self): 215 | def postorderBT(node): 216 | if node is None: 217 | return 218 | postorderBT(node.left) 219 | postorderBT(node.right) 220 | print (node.data, sep=" ", end=" ") 221 | postorderBT(self.root) 222 | print () 223 | 224 | def inorder(self): 225 | def inorderBST(node): 226 | if node is None: 227 | return 228 | inorderBST(node.left) 229 | print (node.data, sep=" ", end=" ") 230 | inorderBST(node.right) 231 | 232 | inorderBST(self.root) 233 | print () 234 | 235 | def isempty(self): 236 | return self.N == 0 237 | -------------------------------------------------------------------------------- /chap6-BinaryTrees/BinaryTreeClient.py: -------------------------------------------------------------------------------- 1 | from BinaryTree import BinaryTree 2 | import random 3 | 4 | 5 | def main(): 6 | bt = BinaryTree() 7 | dataSet = set([]) 8 | # values = ['F', 'B', 'A', 'D', 'C', 'E', 'G', 'I', 'H'] 9 | values = [25, 15, 40, 10, 20, 30, 50, 18, 22, 27, 45, 60, 42, 47, 49] 10 | for r in range(random.randint(7, 12)): 11 | n = random.randint(1, 100) 12 | dataSet.add(n) 13 | 14 | print ("Inserting into Binary Tree: ") 15 | # for data in dataSet: 16 | for data in values: 17 | bt.insert(data) 18 | print (data, sep=" ", end=" ") 19 | print () 20 | 21 | print ("Inorder: ") 22 | bt.inorder() 23 | print ("Preorder: ") 24 | bt.preorder() 25 | print ("postorder: ") 26 | bt.postorder() 27 | print ("Binary Tree Size: ", bt.size()) 28 | print ("Max Depth: ", bt.maxdepth()) 29 | print ("Min value in BST: ", bt.minvalue()) 30 | print ("Max value in BST: ", bt.maxvalue()) 31 | 32 | targetsums = [50, 78, 82, 97, 150] 33 | for target in targetsums: 34 | ans = bt.haspathsum(target) 35 | print ("Sum: %d in binary tree: %s" % (target, ans)) 36 | 37 | print ("print all the root to leaf paths in binary tree") 38 | bt.printpaths() 39 | print ("Mirroring the binary tree: ") 40 | bt.mirror() 41 | bt.inorder() 42 | 43 | bt = BinaryTree() 44 | values = [2, 3, 1] 45 | print ("Inserting into Binary Tree: ") 46 | # for data in dataSet: 47 | for data in values: 48 | bt.insert(data) 49 | print (data, sep=" ", end=" ") 50 | print () 51 | bt.inorder() 52 | print ("After doing double tree: ") 53 | bt.doubletree() 54 | bt.inorder() 55 | 56 | bt1 = BinaryTree() 57 | bt2 = BinaryTree() 58 | # values = ['F', 'B', 'A', 'D', 'C', 'E', 'G', 'I', 'H'] 59 | values = [25, 15, 40, 10, 20, 30, 50, 18, 22, 27, 45, 60, 42, 47, 49] 60 | print ("Inserting into Binary Tree: ") 61 | # for data in dataSet: 62 | for data in values: 63 | bt1.insert(data) 64 | bt2.insert(data) 65 | print (data, sep=" ", end=" ") 66 | print () 67 | bt1.inorder() 68 | bt2.inorder() 69 | comp = bt1.sametree(bt2) 70 | print ("Are the trees same: ", comp) 71 | print ("is bt1 BST: ", bt1.isbst1()) 72 | print ("is bt2 BST: ", bt2.isbst1()) 73 | bt2.mirror() 74 | print ("is bt2 BST after mirroring: ", bt2.isbst1()) 75 | 76 | bt1 = BinaryTree() 77 | bt2 = BinaryTree() 78 | for data in values: 79 | bt1.insert(data) 80 | bt2.insert(data) 81 | print (data, sep=" ", end=" ") 82 | print () 83 | print ("is bt1 BST (efficient version): ", bt1.isbst2()) 84 | print ("is bt2 BST (efficient version): ", bt2.isbst2()) 85 | bt2.mirror() 86 | print ("is bt2 BST after mirroring(efficient version): ", bt2.isbst2()) 87 | 88 | for i in range(1,13): 89 | tot = bt.counttrees(i) 90 | print ("%d nodes: %d unique BSTs" % (i, tot)) 91 | 92 | 93 | if __name__ == '__main__': 94 | main() 95 | --------------------------------------------------------------------------------