├── Ch1. Arrays and Strings ├── Ch 1-1 has duplication.py ├── Ch1-2 is Permutation.py ├── Ch1-3 URLfy.py ├── Ch1-4 is permutation of palidrome.py ├── Ch1-5 Editdistance.py ├── Ch1-6 Compress Letter+count.py ├── Ch1-7 Rotate.py ├── Ch1-8. row and column to zeros.py └── Ch1-9 is string a rotation.py ├── Ch2. Linked-Lists ├── Ch2 -1 Remove duplicate.py ├── Ch2-2 Kth element from the end.py ├── Ch2-3 Delete a node in the middle.py ├── Ch2-4 .divide a list by a pivot.py ├── Ch2-6 sum of two integers represented by linkedlist.py ├── Ch2-7 return Intersection node.py ├── Ch2-8 Detect Cycle.py └── ch2-5 Is palindrome.py ├── Ch3. Stacks-and-Queues ├── 01-three-in-one.py ├── 02-stack-min.py ├── 03-stack-of-plates.py ├── 04-queue-via-stacks.py ├── 05-sort-stack.py └── 06-animal-shelter.py ├── Ch4. Trees-and-Graphs ├── 01-route-between-nodes.py ├── 02-minimal-tree.py ├── 03-list-of-depths.py ├── 04-check-balanced.py ├── 05-validate-bst.py ├── 06-successor.py ├── 07-build-order.py ├── 08-first-ancestor.py ├── 09-bst-sequences.py ├── 10-is-subtree.py ├── 11-random-node.py └── 12-paths-with-sum.py ├── Ch5. Bit-manipulation ├── 01-insertion.py ├── 02-binary-to-string.py ├── 03-flip-bit-to-win.py ├── 04-next-number.py ├── 05-debugger.py ├── 06-conversion.py ├── 07-pairwise-swap.py └── 08-draw-line.py ├── Ch7. OOD ├── 01-deck-of-cards.py ├── 02-call-center.py ├── 03-jukebox.py ├── 04-parking-lot.py ├── 05-online-book-reader.py ├── 06-jigsaw.py ├── 07-chat-server.py ├── 08-othello.py ├── 09-circular-array.py ├── 10-minesweeper.py ├── 11-file-system.py └── 12-hash-table.py ├── Ch8. Recursion-and-Dynamic-Programming ├── 01-triple-step.py ├── 02-robot-in-a-grid.py ├── 03-magic-index.py ├── 04-power-set.py ├── 05-recursive-multiply.py ├── 06-towers-of-hanoi.py ├── 07-permutations-without-dups.py ├── 08-permutations-with-dups.py ├── 09-parens.py ├── 10-paint-fill.py ├── 11-coins.py ├── 12-eight-queens.py ├── 13-stacks-of-boxes.py └── 14-boolean-evaluation.py ├── README.md └── ch-10-sorting-and-searching ├── 01-sorted-merge.py ├── 02-group-anagrams.py ├── 03-search-in-rotated-array.py ├── 04-sorted-search-no-size.py ├── 05-sparse-search.py ├── 06-sort-big-file.py ├── 07-missing-int.py ├── 08-find-duplicates.py ├── 09-sorted-matrix-search.py ├── 10-rank-from-stream.py └── 11-peaks-and-valleys.py /Ch1. Arrays and Strings/Ch 1-1 has duplication.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | def noDupe(string): 4 | a={} 5 | for char in string: 6 | if char in a: 7 | return False 8 | else: 9 | a[char] = 1 10 | return True 11 | 12 | 13 | print (noDupe('aaaaaa')) 14 | print (noDupe('abcdefg')) 15 | print (noDupe('')) 16 | print (noDupe('abcdefghijklmnopqrstuvwxyzz')) 17 | print (noDupe('abcdefghijklmnopqrstuvwxyz')) -------------------------------------------------------------------------------- /Ch1. Arrays and Strings/Ch1-2 is Permutation.py: -------------------------------------------------------------------------------- 1 | from collections import Counter 2 | 3 | def isPermutation(stringA, stringB): 4 | return Counter(stringA) == Counter(stringB) 5 | 6 | print (isPermutation('abc','cba')) 7 | print (isPermutation('abc','bca')) 8 | print (isPermutation('abc','bcaa')) 9 | print (isPermutation('abc','bcax')) 10 | print (isPermutation('abc','b')) -------------------------------------------------------------------------------- /Ch1. Arrays and Strings/Ch1-3 URLfy.py: -------------------------------------------------------------------------------- 1 | def URLfy(string): 2 | return string.strip().replace(' ', '%20') 3 | 4 | print (URLfy('abab abab aaaaaa ')) 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Ch1. Arrays and Strings/Ch1-4 is permutation of palidrome.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | def isPerOfPalindrome(stringA): 3 | Letters = {} 4 | for char in stringA: 5 | if char in Letters: 6 | Letters[char] += 1 7 | else: 8 | Letters[char] = 1 9 | odd = 0 10 | for count in Letters.values(): 11 | if count%2 > 0: 12 | odd += 1 13 | if odd > 1: 14 | return False 15 | return True 16 | 17 | 18 | print (isPerOfPalindrome('racecar')) 19 | print (isPerOfPalindrome('recar')) 20 | print (isPerOfPalindrome('rcecar')) 21 | print (isPerOfPalindrome('rr')) 22 | print (isPerOfPalindrome('')) 23 | 24 | 25 | -------------------------------------------------------------------------------- /Ch1. Arrays and Strings/Ch1-5 Editdistance.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | def isDistanceLessThanTwo(stringA, stringB): 3 | 4 | lenA = len(stringA) 5 | lenB = len(stringB) 6 | dis = 0 7 | if abs(lenA-lenB)>1: 8 | return False 9 | if stringA == stringB:return True 10 | if lenA == lenB: 11 | for i in range(0,lenA): 12 | if stringA[i]!=stringB[i]: 13 | dis += 1 14 | if dis > 1: 15 | return False 16 | return True 17 | else: 18 | if lenA > lenB: 19 | shortS = stringB 20 | longS = stringA 21 | else: 22 | shortS = stringA 23 | longS = stringB 24 | for i in range(0,len(longS)): 25 | temp = longS[:i] + longS[i+1:] 26 | if temp == shortS: 27 | return True 28 | return False 29 | 30 | print (isDistanceLessThanTwo('aaa', 'aaaa')) 31 | print (isDistanceLessThanTwo('aaa', 'aaaaa')) 32 | print (isDistanceLessThanTwo('aaa', 'aabb')) 33 | print (isDistanceLessThanTwo('aaa', 'bbbb')) 34 | print (isDistanceLessThanTwo('', '')) 35 | print (isDistanceLessThanTwo('aaaa', 'aaba')) 36 | print (isDistanceLessThanTwo('aaaa', 'babb')) 37 | 38 | 39 | -------------------------------------------------------------------------------- /Ch1. Arrays and Strings/Ch1-6 Compress Letter+count.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | def compress(string): 3 | if string == '': 4 | return '' 5 | current = string[0] 6 | result = '' 7 | count = 0 8 | string = string + ' ' 9 | for i in range (0, len(string)): 10 | if current == string[i]: 11 | count +=1 12 | else: 13 | result += current+str(count) 14 | count = 1 15 | current = string[i] 16 | return result 17 | 18 | 19 | print (compress('aaabbbcb')) 20 | print (compress('ab')) 21 | print (compress('a')) 22 | print (compress('')) 23 | print (compress('aaabbbcbabababababababababaxxxxxxxxxxxxxxxxxxxx')) 24 | -------------------------------------------------------------------------------- /Ch1. Arrays and Strings/Ch1-7 Rotate.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | def rotate_matrix(m): 3 | l = len(m) 4 | rotate = [None]*l 5 | for row in range (0,l): 6 | rotate[row] = [None]*l 7 | for i in range (0, l): 8 | for j in range (0, l): 9 | rotate[l-i-1][j] = m[j][i] 10 | 11 | return rotate 12 | 13 | class Test(unittest.TestCase): 14 | def test_rotate_matrix(self): 15 | mat1 = [[1,2],[3,4]] 16 | mat2 = [[2,4],[1,3]] 17 | self.assertEqual(rotate_matrix(mat1), mat2) 18 | mat3 = [[1,2,3],[4,5,6],[7,8,9]] 19 | mat4 = [[3,6,9],[2,5,8],[1,4,7]] 20 | self.assertEqual(rotate_matrix(mat3), mat4) 21 | mat5 = [[1,2,3,4],[5,6,7,8],[9,10,11,12],[13,14,15,16]] 22 | mat6 = [[4,8,12,16],[3,7,11,15],[2,6,10,14],[1,5,9,13]] 23 | self.assertEqual(rotate_matrix(mat5), mat6) 24 | 25 | unittest.main() 26 | -------------------------------------------------------------------------------- /Ch1. Arrays and Strings/Ch1-8. row and column to zeros.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import unittest 3 | def zero_out_row_col(m): 4 | h = len(m) 5 | l = len(m[0]) 6 | ipositions = {} 7 | jpositions = {} 8 | 9 | for i in range (0, h): 10 | for j in range(0, l): 11 | if m[i][j] == 0: 12 | ipositions[i] = 1 13 | jpositions[j] = 1 14 | 15 | for i in ipositions.keys(): 16 | for j in range (0, l): 17 | m[i][j] = 0 18 | for j in jpositions.keys(): 19 | for i in range (0, h): 20 | m[i][j] = 0 21 | 22 | class Test(unittest.TestCase): 23 | def test_zero_out_row_col_matrix(self): 24 | mat1 = [[1,1,1,1,1],[1,0,1,1,1],[1,1,1,1,1],[1,1,1,0,1],[2,3,4,5,6]] 25 | mat2 = [[1,0,1,0,1],[0,0,0,0,0],[1,0,1,0,1],[0,0,0,0,0],[2,0,4,0,6]] 26 | zero_out_row_col(mat1) 27 | self.assertEqual(mat1, mat2) 28 | 29 | mat1 = [[1,1,1],[1,0,1],[1,1,1]] 30 | mat2 = [[1,0,1],[0,0,0],[1,0,1]] 31 | zero_out_row_col(mat1) 32 | self.assertEqual(mat1, mat2) 33 | 34 | unittest.main() -------------------------------------------------------------------------------- /Ch1. Arrays and Strings/Ch1-9 is string a rotation.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | def is_rotation(stringA,stringB): 4 | if len(stringA) != len(stringB): 5 | return False 6 | else: 7 | return is_substring(stringA + stringA, stringB) 8 | 9 | 10 | def is_substring(stringA, stringB): 11 | lenA = len(stringA) 12 | lenB = len(stringB) 13 | for i in range(0,lenA-lenB+1): 14 | if stringA[i:i+lenB] == stringB: 15 | return True 16 | return False 17 | 18 | class Test(unittest.TestCase): 19 | def test_is_rotation(self): 20 | s1 = "tabletop" 21 | s2 = "toptable" 22 | s3 = "optalbet" 23 | self.assertTrue(is_rotation(s1, s2)) 24 | self.assertFalse(is_rotation(s1, s3)) 25 | 26 | def test_is_substring(self): 27 | s1 = "cat in the hat" 28 | s2 = "cat" 29 | s3 = "hat" 30 | s4 = "cats" 31 | self.assertTrue(is_substring(s1, s2)) 32 | self.assertTrue(is_substring(s1, s3)) 33 | self.assertFalse(is_substring(s1, s4)) 34 | 35 | unittest.main() 36 | -------------------------------------------------------------------------------- /Ch2. Linked-Lists/Ch2 -1 Remove duplicate.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import unittest 3 | 4 | def remove_duplicates(head): 5 | nodes = {} 6 | node = head 7 | while(node!=None): 8 | if node.data in nodes.keys(): 9 | node.data = node.next.data 10 | node.next = node.next.next 11 | else: 12 | nodes[node.data] = 1 13 | node = node.next 14 | 15 | return head 16 | 17 | 18 | 19 | 20 | 21 | 22 | class Node(): 23 | def __init__(self, data, next): 24 | self.data = data 25 | self.next = next 26 | 27 | class Test(unittest.TestCase): 28 | def test_remove_duplicates(self): 29 | head = Node(1,Node(3,Node(3,Node(1,Node(5,None))))) 30 | remove_duplicates(head) 31 | self.assertEqual(head.data, 1) 32 | self.assertEqual(head.next.data, 3) 33 | self.assertEqual(head.next.next.data, 5) 34 | self.assertEqual(head.next.next.next, None) 35 | 36 | 37 | unittest.main() -------------------------------------------------------------------------------- /Ch2. Linked-Lists/Ch2-2 Kth element from the end.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import unittest 4 | 5 | 6 | def kth_to_last(head, k): 7 | if k == 0: return None 8 | pA = head 9 | pB = head 10 | for i in range (0,k): 11 | if not pA: 12 | return None 13 | pA = pA.next 14 | while pA: 15 | pA, pB = pA.next, pB.next 16 | return pB 17 | 18 | class Node(): 19 | def __init__(self, data, next=None): 20 | self.data = data 21 | self.next = next 22 | 23 | class Test(unittest.TestCase): 24 | def test_kth_to_last(self): 25 | head = Node(1,Node(2,Node(3,Node(4,Node(5,Node(6,Node(7))))))) 26 | self.assertEqual(None, kth_to_last(head, 0)); 27 | self.assertEqual(7, kth_to_last(head, 1).data); 28 | self.assertEqual(4, kth_to_last(head, 4).data); 29 | self.assertEqual(2, kth_to_last(head, 6).data); 30 | self.assertEqual(1, kth_to_last(head, 7).data); 31 | self.assertEqual(None, kth_to_last(head, 8)); 32 | 33 | unittest.main() -------------------------------------------------------------------------------- /Ch2. Linked-Lists/Ch2-3 Delete a node in the middle.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import unittest 4 | def delete_middle(node): 5 | node.data = node.next.data 6 | node.next = node.next.next 7 | 8 | class Node(): 9 | def __init__(self, data, next=None): 10 | self.data, self.next = data, next 11 | 12 | class Test(unittest.TestCase): 13 | def test_delete_middle(self): 14 | head = Node(1,Node(2,Node(3,Node(4)))) 15 | delete_middle(head.next.next) 16 | self.assertEqual(head.data, 1) 17 | self.assertEqual(head.next.data, 2) 18 | self.assertEqual(head.next.next.data, 4) 19 | 20 | unittest.main() -------------------------------------------------------------------------------- /Ch2. Linked-Lists/Ch2-4 .divide a list by a pivot.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | def partition(head, p): 4 | node = head 5 | headA = None 6 | headB = None 7 | tailA = None 8 | while node: 9 | if node.data < p: 10 | if headA ==None: 11 | pA = node 12 | headA = pA 13 | else: 14 | pA.next = node 15 | pA = pA.next 16 | tailA = node 17 | else: 18 | if headB == None: 19 | pB = node 20 | headB = pB 21 | else: 22 | pB.next = node 23 | pB = pB.next 24 | node = node.next 25 | tailA.next = headB 26 | return headA 27 | 28 | 29 | 30 | class Node(): 31 | def __init__(self, data, next=None): 32 | self.data, self.next = data, next 33 | 34 | def __str__(self): 35 | string = str(self.data) 36 | if self.next: 37 | string += ',' + str(self.next) 38 | return string 39 | 40 | class Test(unittest.TestCase): 41 | def test_partition(self): 42 | head1 = Node(7,Node(2,Node(9,Node(1,Node(6,Node(3,Node(8))))))) 43 | head2 = partition(head1, 6) 44 | self.assertEqual(str(head2), "2,1,3,7,9,6,8") 45 | head3 = partition(head2, 7) 46 | self.assertEqual(str(head3), "2,1,3,6,7,9,8") 47 | 48 | unittest.main() -------------------------------------------------------------------------------- /Ch2. Linked-Lists/Ch2-6 sum of two integers represented by linkedlist.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | def sum_lists(num1, num2): 4 | result = Node((num1.data+num2.data)%10) 5 | pA = result 6 | carry = int((num1.data+num2.data)/10) 7 | while num1 or num2: 8 | if num1: 9 | num1 = num1.next 10 | if num1:a = num1.data 11 | else: a = 0 12 | if num2: 13 | num2 = num2.next 14 | if num2:b = num2.data 15 | else: b = 0 16 | if num2 == None and num1 == None : break 17 | pA.next = Node((a+b+carry)%10) 18 | pA = pA.next 19 | carry = int((a+b+carry)/10) 20 | 21 | return result 22 | 23 | 24 | class Node(): 25 | def __init__(self, data, next=None): 26 | self.data, self.next = data, next 27 | 28 | def __str__(self): 29 | string = str(self.data) 30 | if self.next: 31 | string += ',' + str(self.next) 32 | return string 33 | 34 | class Test(unittest.TestCase): 35 | def test_sum_lists(self): 36 | num1 = Node(1,Node(2,Node(3))) 37 | num2 = Node(4,Node(9,Node(5))) 38 | self.assertEqual(str(sum_lists(num1, num2)), "5,1,9") 39 | num1 = Node(9,Node(2,Node(3,Node(4,Node(1))))) 40 | num2 = Node(4,Node(9,Node(8))) 41 | self.assertEqual(str(sum_lists(num1, num2)), "3,2,2,5,1") 42 | 43 | unittest.main() -------------------------------------------------------------------------------- /Ch2. Linked-Lists/Ch2-7 return Intersection node.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import unittest 3 | 4 | def intersection(head1, head2): 5 | A = {} 6 | while head1: 7 | A[head1] = 1 8 | head1= head1.next 9 | while head2: 10 | if head2 in A: 11 | return head2 12 | head2 = head2.next 13 | return None 14 | 15 | 16 | class Node(): 17 | def __init__(self, data, next=None): 18 | self.data, self.next = data, next 19 | 20 | def __str__(self): 21 | string = str(self.data) 22 | if self.next: 23 | string += ',' + str(self.next) 24 | return string 25 | 26 | class Test(unittest.TestCase): 27 | def test_intersection(self): 28 | head1 = Node(10,Node(20,Node(30))) 29 | head2 = Node(20,Node(30,Node(40))) 30 | self.assertEqual(intersection(head1, head2), None) 31 | node = Node(70,Node(80)) 32 | head3 = Node(50,Node(20,node)) 33 | head4 = Node(60,Node(90,Node(10,node))) 34 | self.assertEqual(intersection(head3, head4), node) 35 | 36 | unittest.main() 37 | -------------------------------------------------------------------------------- /Ch2. Linked-Lists/Ch2-8 Detect Cycle.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | def detect_cycle(head): 4 | pA = head 5 | pB = head.next 6 | A = {} 7 | while pB: 8 | A[pA] = 1 9 | if pB in A: 10 | return pB 11 | pA = pA.next 12 | pB = pB.next 13 | return None 14 | 15 | class Node(): 16 | def __init__(self, data, next=None): 17 | self.data, self.next = data, next 18 | 19 | class Test(unittest.TestCase): 20 | def test_detect_cycle(self): 21 | head1 = Node(100,Node(200,Node(300))) 22 | self.assertEqual(detect_cycle(head1), None) 23 | node1 = Node(600) 24 | node2 = Node(700,Node(800,Node(900,node1))) 25 | node1.next = node2 26 | head2 = Node(500,node1) 27 | self.assertEqual(detect_cycle(head2), node1) 28 | 29 | unittest.main() -------------------------------------------------------------------------------- /Ch2. Linked-Lists/ch2-5 Is palindrome.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | def is_palindrome(head): 4 | node1 = head 5 | node2 = copy_reverse(head) 6 | while node1: 7 | if node1.data != node2.data: 8 | return False 9 | node1 = node1.next 10 | node2 = node2.next 11 | return True 12 | 13 | def copy_reverse(head): 14 | firstnode = copy(head) 15 | secondnode = None 16 | while firstnode: 17 | temp = copy(firstnode.next) 18 | firstnode.next = secondnode 19 | secondnode = firstnode 20 | firstnode = temp 21 | return secondnode 22 | 23 | def copy(node): 24 | if node == None: return None 25 | return Node(node.data, node.next) 26 | 27 | class Node(): 28 | def __init__(self, data, next=None): 29 | self.data, self.next = data, next 30 | 31 | def __str__(self): 32 | string = str(self.data) 33 | if self.next: 34 | string += ',' + str(self.next) 35 | return string 36 | 37 | class Test(unittest.TestCase): 38 | def test_palindrome(self): 39 | list1 = Node(10) 40 | self.assertTrue(is_palindrome(list1)) 41 | list2 = Node(10,Node(10)) 42 | self.assertTrue(is_palindrome(list2)) 43 | list3 = Node(10,Node(20)) 44 | self.assertFalse(is_palindrome(list3)) 45 | list4 = Node(10,Node(70,Node(30,Node(70,Node(10))))) 46 | self.assertTrue(is_palindrome(list4)) 47 | 48 | def test_copy_reverse(self): 49 | head = Node(10,Node(20,Node(30,Node(40)))) 50 | self.assertEqual(str(copy_reverse(head)), "40,30,20,10") 51 | 52 | unittest.main() -------------------------------------------------------------------------------- /Ch3. Stacks-and-Queues/01-three-in-one.py: -------------------------------------------------------------------------------- 1 | # Use a single array to implement three stacks. 2 | 3 | class ThreeStacks(): 4 | def __init__(self): 5 | self.array = [None, None, None] 6 | self.current = [0, 1, 2] 7 | 8 | def push(self, item, stack_number): 9 | if not stack_number in [0, 1, 2]: 10 | raise Exception("Bad stack number") 11 | while len(self.array) <= self.current[stack_number]: 12 | self.array += [None] * len(self.array) 13 | self.array[self.current[stack_number]] = item 14 | self.current[stack_number] += 3 15 | 16 | def pop(self, stack_number): 17 | if not stack_number in [0, 1, 2]: 18 | raise Exception("Bad stack number") 19 | if self.current[stack_number] > 3: 20 | self.current[stack_number] -= 3 21 | item = self.array[self.current[stack_number]] 22 | self.array[self.current[stack_number]] = None 23 | return item 24 | 25 | import unittest 26 | 27 | class Test(unittest.TestCase): 28 | def test_three_stacks(self): 29 | three_stacks = ThreeStacks() 30 | three_stacks.push(101, 0) 31 | three_stacks.push(102, 0) 32 | three_stacks.push(103, 0) 33 | three_stacks.push(201, 1) 34 | self.assertEqual(three_stacks.pop(0), 103) 35 | self.assertEqual(three_stacks.pop(1), 201) 36 | self.assertEqual(three_stacks.pop(1), None) 37 | self.assertEqual(three_stacks.pop(2), None) 38 | three_stacks.push(301, 2) 39 | three_stacks.push(302, 2) 40 | self.assertEqual(three_stacks.pop(2), 302) 41 | self.assertEqual(three_stacks.pop(2), 301) 42 | self.assertEqual(three_stacks.pop(2), None) 43 | 44 | if __name__ == "__main__": 45 | unittest.main() 46 | -------------------------------------------------------------------------------- /Ch3. Stacks-and-Queues/02-stack-min.py: -------------------------------------------------------------------------------- 1 | # Implement a stack with a function that returns the current minimum item. 2 | 3 | class MinStack(): 4 | def __init__(self): 5 | self.top, self._min = None, None 6 | 7 | def min(self): 8 | if not self._min: 9 | return None 10 | return self._min.data 11 | 12 | def push(self, item): 13 | if self._min and (self._min.data < item): 14 | self._min = Node(data=self._min.data, next=self._min) 15 | else: 16 | self._min = Node(data=item, next=self._min) 17 | self.top = Node(data=item, next=self.top) 18 | 19 | def pop(self): 20 | if not self.top: 21 | return None 22 | self._min = self._min.next 23 | item = self.top.data 24 | self.top = self.top.next 25 | return item 26 | 27 | class Node(): 28 | def __init__(self, data=None, next=None): 29 | self.data, self.next = data, next 30 | 31 | def __str__(self): 32 | string = str(self.data) 33 | if self.next: 34 | string += ',' + str(self.next) 35 | return string 36 | 37 | import unittest 38 | 39 | class Test(unittest.TestCase): 40 | def test_min_stack(self): 41 | min_stack = MinStack() 42 | self.assertEqual(min_stack.min(), None) 43 | min_stack.push(7) 44 | self.assertEqual(min_stack.min(), 7) 45 | min_stack.push(6) 46 | min_stack.push(5) 47 | self.assertEqual(min_stack.min(), 5) 48 | min_stack.push(10) 49 | self.assertEqual(min_stack.min(), 5) 50 | self.assertEqual(min_stack.pop(), 10) 51 | self.assertEqual(min_stack.pop(), 5) 52 | self.assertEqual(min_stack.min(), 6) 53 | self.assertEqual(min_stack.pop(), 6) 54 | self.assertEqual(min_stack.pop(), 7) 55 | self.assertEqual(min_stack.min(), None) 56 | 57 | if __name__ == "__main__": 58 | unittest.main() 59 | 60 | -------------------------------------------------------------------------------- /Ch3. Stacks-and-Queues/03-stack-of-plates.py: -------------------------------------------------------------------------------- 1 | # Implement a class that acts as a single stack made out of multiple stacks 2 | # which each have a set capacity. 3 | 4 | class MultiStack(): 5 | def __init__(self, capacity): 6 | self.capacity = capacity 7 | self.stacks = [] 8 | 9 | def push(self, item): 10 | if len(self.stacks) and (len(self.stacks[-1])) < self.capacity: 11 | self.stacks[-1].append(item) 12 | else: 13 | self.stacks.append([item]) 14 | 15 | def pop(self): 16 | while len(self.stacks)and len(self.stacks[-1]) == 0: 17 | self.stacks.pop() 18 | if len(self.stacks) == 0: 19 | return None 20 | else: 21 | return self.stacks[-1].pop() 22 | 23 | 24 | def pop_at(self, stack_number): 25 | if len(self.stacks[stack_number]) > 0: 26 | return self.stacks[stack_number].pop() 27 | else: 28 | return None 29 | 30 | import unittest 31 | 32 | class Test(unittest.TestCase): 33 | def test_multi_stack(self): 34 | stack = MultiStack(3) 35 | stack.push(11) 36 | stack.push(22) 37 | stack.push(33) 38 | stack.push(44) 39 | stack.push(55) 40 | stack.push(66) 41 | stack.push(77) 42 | stack.push(88) 43 | self.assertEqual(stack.pop(), 88) 44 | self.assertEqual(stack.pop_at(1), 66) 45 | self.assertEqual(stack.pop_at(0), 33) 46 | self.assertEqual(stack.pop_at(1), 55) 47 | self.assertEqual(stack.pop_at(1), 44) 48 | self.assertEqual(stack.pop_at(1), None) 49 | stack.push(99) 50 | self.assertEqual(stack.pop(), 99) 51 | self.assertEqual(stack.pop(), 77) 52 | self.assertEqual(stack.pop(), 22) 53 | self.assertEqual(stack.pop(), 11) 54 | self.assertEqual(stack.pop(), None) 55 | 56 | if __name__ == "__main__": 57 | unittest.main() 58 | 59 | -------------------------------------------------------------------------------- /Ch3. Stacks-and-Queues/04-queue-via-stacks.py: -------------------------------------------------------------------------------- 1 | # Implement a queue using two stacks. 2 | 3 | class QueueViaStacks(): 4 | def __init__(self): 5 | self.inStack = Stack() 6 | self.outStack = Stack() 7 | 8 | def add(self, item): 9 | self.inStack.push(item) 10 | 11 | def remove(self): 12 | while len(self.inStack): 13 | self.outStack.push(self.inStack.pop()) 14 | result = self.outStack.pop() 15 | while len(self.outStack): 16 | self.inStack.push(self.outStack.pop()) 17 | return result 18 | 19 | class Stack(): 20 | def __init__(self): 21 | self.array = [] 22 | 23 | def __len__(self): 24 | return len(self.array) 25 | 26 | def push(self, item): 27 | self.array.append(item) 28 | 29 | def pop(self): 30 | if not len(self.array): 31 | return None 32 | return self.array.pop() 33 | 34 | import unittest 35 | 36 | class Test(unittest.TestCase): 37 | def test_queue_via_stacks(self): 38 | queue = QueueViaStacks() 39 | queue.add(11) 40 | queue.add(22) 41 | queue.add(33) 42 | self.assertEqual(queue.remove(), 11) 43 | queue.add(44) 44 | queue.add(55) 45 | queue.add(66) 46 | self.assertEqual(queue.remove(), 22) 47 | self.assertEqual(queue.remove(), 33) 48 | self.assertEqual(queue.remove(), 44) 49 | self.assertEqual(queue.remove(), 55) 50 | queue.add(77) 51 | self.assertEqual(queue.remove(), 66) 52 | self.assertEqual(queue.remove(), 77) 53 | self.assertEqual(queue.remove(), None) 54 | 55 | if __name__ == "__main__": 56 | unittest.main() 57 | 58 | -------------------------------------------------------------------------------- /Ch3. Stacks-and-Queues/05-sort-stack.py: -------------------------------------------------------------------------------- 1 | # Sort a stack with the smallest on top using only a single temporary stack. 2 | 3 | def sort_stack(stack): 4 | previous = stack.pop() 5 | current = stack.pop() 6 | temp = Stack() 7 | while current: 8 | if previous < current: 9 | temp.push(previous) 10 | previous = current 11 | current = stack.pop() 12 | else: 13 | temp.push(current) 14 | current = stack.pop() 15 | if current == None and previous: temp.push(previous) 16 | 17 | sorted = True 18 | previous = temp.pop() 19 | current = temp.pop() 20 | while current: 21 | if previous > current: 22 | stack.push(previous) 23 | previous = current 24 | current = temp.pop() 25 | else: 26 | stack.push(current) 27 | current = temp.pop() 28 | sorted = False 29 | if current == None and previous: stack.push(previous) 30 | if sorted: return stack 31 | else: return sort_stack(stack) 32 | 33 | class Stack(): 34 | def __init__(self): 35 | self.top = None 36 | 37 | def __str__(self): 38 | return str(self.top) 39 | 40 | def push(self, item): 41 | self.top = current(item, self.top) 42 | 43 | def pop(self): 44 | if not self.top: 45 | return None 46 | item = self.top 47 | self.top = self.top.next 48 | return item.data 49 | 50 | class current(): 51 | def __init__(self, data=None, next=None): 52 | self.data, self.next = data, next 53 | 54 | def __str__(self): 55 | return str(self and self.data) + ',' + str(self and self.next) 56 | 57 | import unittest 58 | 59 | class Test(unittest.TestCase): 60 | def test_sort_stack(self): 61 | self.assertEqual(str(sort_stack(Stack())), "None") 62 | stack = Stack() 63 | stack.push(10) 64 | stack.push(30) 65 | stack.push(70) 66 | stack.push(40) 67 | stack.push(80) 68 | stack.push(20) 69 | stack.push(90) 70 | stack.push(50) 71 | stack.push(60) 72 | self.assertEqual(str(stack), "60,50,90,20,80,40,70,30,10,None") 73 | self.assertEqual(str(sort_stack(stack)), "10,20,30,40,50,60,70,80,90,None") 74 | 75 | unittest.main() -------------------------------------------------------------------------------- /Ch3. Stacks-and-Queues/06-animal-shelter.py: -------------------------------------------------------------------------------- 1 | # Implement a cat and dog queue for an animal shelter. 2 | 3 | class AnimalShelter(): 4 | def __init__(self): 5 | self.cats, self.dogs = [], [] 6 | 7 | def enqueue(self, animal): 8 | if animal.__class__ == Cat: self.cats.append(animal) 9 | else: self.dogs.append(animal) 10 | 11 | def dequeueAny(self): 12 | if len(self.cats): return self.dequeueCat() 13 | return self.dequeueDog() 14 | 15 | def dequeueCat(self): 16 | if len(self.cats) == 0: return None 17 | cat = self.cats[0] 18 | self.cats = self.cats[1:] 19 | return cat 20 | 21 | def dequeueDog(self): 22 | if len(self.dogs) == 0: return None 23 | dog = self.dogs[0] 24 | self.dogs = self.dogs[1:] 25 | return dog 26 | 27 | class Animal(): 28 | def __init__(self, name): 29 | self.name = name 30 | 31 | def __str__(self): 32 | return self.name 33 | 34 | class Cat(Animal): pass 35 | class Dog(Animal): pass 36 | 37 | import unittest 38 | 39 | class Test(unittest.TestCase): 40 | def test_animal_shelter(self): 41 | shelter = AnimalShelter() 42 | shelter.enqueue(Cat("Hanzack")) 43 | shelter.enqueue(Dog("Pluto")) 44 | shelter.enqueue(Cat("Garfield")) 45 | shelter.enqueue(Cat("Tony")) 46 | shelter.enqueue(Dog("Clifford")) 47 | shelter.enqueue(Dog("Blue")) 48 | self.assertEqual(str(shelter.dequeueAny()), "Hanzack") 49 | self.assertEqual(str(shelter.dequeueAny()), "Garfield") 50 | self.assertEqual(str(shelter.dequeueDog()), "Pluto") 51 | self.assertEqual(str(shelter.dequeueDog()), "Clifford") 52 | self.assertEqual(str(shelter.dequeueCat()), "Tony") 53 | self.assertEqual(str(shelter.dequeueCat()), "None") 54 | self.assertEqual(str(shelter.dequeueAny()), "Blue") 55 | self.assertEqual(str(shelter.dequeueAny()), "None") 56 | 57 | if __name__ == "__main__": 58 | unittest.main() 59 | 60 | -------------------------------------------------------------------------------- /Ch4. Trees-and-Graphs/01-route-between-nodes.py: -------------------------------------------------------------------------------- 1 | # Find a route from the first node to the second node in a directed graph. 2 | 3 | def find_route(node1, node2): 4 | queue = Queue() 5 | pathFound = None 6 | node = node1 7 | node.shortest_path = [node] 8 | all_visited_nodes = [node] 9 | while node: 10 | for adjacent in node.adjacency_list: 11 | if adjacent.shortest_path == None: 12 | adjacent.shortest_path = node.shortest_path + [adjacent] 13 | if adjacent == node2: 14 | pathFound = node.shortest_path + [adjacent] 15 | break 16 | queue.add(adjacent) 17 | all_visited_nodes.append(adjacent) 18 | node = queue.remove() 19 | for visited in all_visited_nodes: 20 | visited.shortest_path = None 21 | return pathFound 22 | 23 | class Node(): 24 | def __init__(self, data, adjacency_list=None): 25 | self.data = data 26 | self.adjacency_list = adjacency_list or [] 27 | self.shortest_path = None 28 | 29 | def add_edge_to(self, node): 30 | self.adjacency_list += [node] 31 | 32 | def __str__(self): 33 | return self.data 34 | 35 | class Queue(): 36 | def __init__(self): 37 | self.array = [] 38 | 39 | def add(self, item): 40 | self.array.append(item) 41 | 42 | def remove(self): 43 | if not len(self.array): 44 | return None 45 | item = self.array[0] 46 | del self.array[0] 47 | return item 48 | 49 | def isEmpty(self): 50 | if len(self.array) == 0: 51 | return True 52 | else: return False 53 | 54 | import unittest 55 | 56 | def str_for(path): 57 | if not path: return str(path) 58 | return ''.join([str(n) for n in path]) 59 | 60 | class Test(unittest.TestCase): 61 | def test_find_route(self): 62 | node_j = Node('J') 63 | node_i = Node('I') 64 | node_h = Node('H') 65 | node_d = Node('D') 66 | node_f = Node('F', [node_i]) 67 | node_b = Node('B', [node_j]) 68 | node_g = Node('G', [node_d, node_h]) 69 | node_c = Node('C', [node_g]) 70 | node_a = Node('A', [node_b, node_c, node_d]) 71 | node_e = Node('E', [node_f, node_a]) 72 | node_d.add_edge_to(node_a) 73 | self.assertEqual(str_for(find_route(node_a, node_i)), 'None') 74 | self.assertEqual(str_for(find_route(node_a, node_j)), 'ABJ') 75 | node_h.add_edge_to(node_i) 76 | self.assertEqual(str_for(find_route(node_a, node_i)), 'ACGHI') 77 | 78 | if __name__ == "__main__": 79 | unittest.main() 80 | 81 | -------------------------------------------------------------------------------- /Ch4. Trees-and-Graphs/02-minimal-tree.py: -------------------------------------------------------------------------------- 1 | # Create a minimal height binary search tree with the elements of a given 2 | # sorted array. 3 | 4 | def minimal_height_bst(sorted_array): 5 | if len(sorted_array) == 0: return None 6 | if len(sorted_array) == 1: return BSTNode(sorted_array[0]) 7 | mid = int(len(sorted_array)/2) 8 | left = minimal_height_bst(sorted_array[:mid]) 9 | right = minimal_height_bst(sorted_array[mid+1:]) 10 | return BSTNode(sorted_array[mid], left, right) 11 | 12 | class BSTNode(): 13 | def __init__(self, data=None, left=None, right=None): 14 | self.data, self.left, self.right = data, left, right 15 | 16 | def __str__(self): 17 | string = "(" + str(self.data) 18 | if self.left: string += str(self.left) 19 | else: string += "." 20 | if self.right: string += str(self.right) 21 | else: string += "." 22 | return string + ")" 23 | 24 | import unittest 25 | 26 | class Test(unittest.TestCase): 27 | def test_minimal_height_bst(self): 28 | sorted_array = [1, 2, 3, 4, 5, 6, 7, 8, 9] 29 | bst = minimal_height_bst(sorted_array) 30 | self.assertEqual(str(bst), "(5(3(2(1..).)(4..))(8(7(6..).)(9..)))") 31 | 32 | if __name__ == "__main__": 33 | unittest.main() 34 | 35 | -------------------------------------------------------------------------------- /Ch4. Trees-and-Graphs/03-list-of-depths.py: -------------------------------------------------------------------------------- 1 | # Return an array of linked lists containing all elements on each depth 2 | # of a binary tree. 3 | 4 | def list_of_depths(binary_tree): 5 | if not binary_tree: 6 | return [] 7 | lists = [] 8 | tail = None 9 | node = binary_tree 10 | queue = Queue() 11 | depth = -1 12 | node.depth = 0 13 | while node: 14 | if node.depth == depth: 15 | tail.next = ListNode(node.data) 16 | tail = tail.next 17 | else: 18 | depth = node.depth 19 | tail = ListNode(node.data) 20 | lists.append(tail) 21 | for child in (node.left, node.right): 22 | if child: 23 | child.depth = node.depth + 1 24 | queue.add(child) 25 | node = queue.remove() 26 | return lists 27 | 28 | 29 | 30 | class TreeNode(): 31 | def __init__(self, data=None, left=None, right=None): 32 | self.data, self.left, self.right = data, left, right 33 | self.depth = None 34 | 35 | class ListNode(): 36 | def __init__(self, data=None, next=None): 37 | self.data, self.next = data, next 38 | 39 | def __str__(self): 40 | return str(self.data) + ',' + str(self.next) 41 | 42 | class Queue(): 43 | def __init__(self): 44 | self.head, self.tail = None, None 45 | 46 | def add(self, item): 47 | if self.head: 48 | self.tail.next = ListNode(item) 49 | self.tail = self.tail.next 50 | else: 51 | self.head = self.tail = ListNode(item) 52 | 53 | def remove(self): 54 | if not self.head: 55 | return None 56 | item = self.head.data 57 | self.head = self.head.next 58 | return item 59 | 60 | import unittest 61 | 62 | class Test(unittest.TestCase): 63 | def test_list_of_depths(self): 64 | node_h = TreeNode('H') 65 | node_g = TreeNode('G') 66 | node_f = TreeNode('F') 67 | node_e = TreeNode('E', node_g) 68 | node_d = TreeNode('D', node_h) 69 | node_c = TreeNode('C', None, node_f) 70 | node_b = TreeNode('B', node_d, node_e) 71 | node_a = TreeNode('A', node_b, node_c) 72 | lists = list_of_depths(node_a) 73 | self.assertEqual(str(lists[0]), "A,None") 74 | self.assertEqual(str(lists[1]), "B,C,None") 75 | self.assertEqual(str(lists[2]), "D,E,F,None") 76 | self.assertEqual(str(lists[3]), "H,G,None") 77 | self.assertEqual(len(lists), 4) 78 | 79 | if __name__ == "__main__": 80 | unittest.main() 81 | 82 | -------------------------------------------------------------------------------- /Ch4. Trees-and-Graphs/04-check-balanced.py: -------------------------------------------------------------------------------- 1 | # Tell whether or not a binary tree is balanced. 2 | 3 | def is_balanced(binary_tree): 4 | if not binary_tree: 5 | return (True, 0) 6 | left_balance, left_depth = is_balanced(binary_tree.left) 7 | if not left_balance: 8 | return (False, None) 9 | right_balance, right_depth = is_balanced(binary_tree.right) 10 | if not right_balance or abs(right_depth-left_depth) > 1: 11 | return (False, None) 12 | return (True, max(right_depth, left_depth)+1) 13 | 14 | class Node(): 15 | def __init__(self, left=None, right=None): 16 | self.left, self.right = left, right 17 | 18 | import unittest 19 | 20 | class Test(unittest.TestCase): 21 | def test_is_balanced(self): 22 | self.assertEqual(is_balanced(Node(Node(),Node())), (True, 2)) 23 | self.assertEqual(is_balanced(Node(Node(),Node(Node()))), (True, 3)) 24 | self.assertEqual(is_balanced(Node(Node(),Node(Node(Node())))), 25 | (False, None)) 26 | self.assertEqual(is_balanced(Node(Node(Node()),Node(Node(Node())))), 27 | (False,None)) 28 | self.assertEqual(is_balanced(Node(Node(Node()), 29 | Node(Node(Node()),Node()))), (True, 4)) 30 | 31 | if __name__ == "__main__": 32 | unittest.main() 33 | -------------------------------------------------------------------------------- /Ch4. Trees-and-Graphs/05-validate-bst.py: -------------------------------------------------------------------------------- 1 | # Validate that a binary tree is a binary search tree. 2 | 3 | def validate_tree(binary_tree): 4 | return validate_tree_node(binary_tree, -float('inf'), float('inf')) 5 | 6 | def validate_tree_node(node, left_bound, right_bound): 7 | if not node: return True 8 | return node.data >= left_bound and node.data <= right_bound and validate_tree_node(node.left, left_bound, node.data)and validate_tree_node(node.right, node.data, right_bound) 9 | 10 | 11 | class Node(): 12 | def __init__(self, data, left=None, right=None): 13 | self.data, self.left, self.right = data, left, right 14 | 15 | import unittest 16 | 17 | class Test(unittest.TestCase): 18 | def test_validate_tree(self): 19 | self.assertEqual(validate_tree(Node(3,Node(1),Node(8))), True) 20 | tree1 = Node(5,Node(3,Node(1),Node(4)),Node(7,Node(6),Node(8,None,Node(9)))) 21 | self.assertEqual(validate_tree(tree1), True) 22 | tree2 = Node(7,Node(3,Node(1),Node(8)),Node(9,Node(8),Node(11))) 23 | self.assertEqual(validate_tree(tree2), False) 24 | 25 | if __name__ == "__main__": 26 | unittest.main() 27 | 28 | -------------------------------------------------------------------------------- /Ch4. Trees-and-Graphs/06-successor.py: -------------------------------------------------------------------------------- 1 | # Return the successor of a node in a binary search tree. 2 | 3 | def successor(node): 4 | if not node: return None 5 | child = node.right 6 | if child: 7 | while child.left: 8 | child = child.left 9 | if child: 10 | return child 11 | parent = node.parent 12 | child = node 13 | if parent and parent.left == child: 14 | return parent 15 | if parent and parent.right == child: 16 | while parent and parent.left != child: 17 | child = parent 18 | parent = child.parent 19 | return parent 20 | 21 | class Node(): 22 | def __init__(self, data, left=None, right=None): 23 | self.data, self.left, self.right = data, left, right 24 | self.parent = None 25 | if self.left: self.left.parent = self 26 | if self.right: self.right.parent = self 27 | 28 | import unittest 29 | 30 | class Test(unittest.TestCase): 31 | def test_successor(self): 32 | self.assertEqual(successor(Node(22, Node(11))), None) 33 | self.assertEqual(successor(Node(22, Node(11), Node(33))).data, 33) 34 | self.assertEqual(successor(Node(22, Node(11), Node(33, Node(28)))).data, 28) 35 | self.assertEqual(successor(Node(22, Node(11), Node(33)).left).data, 22) 36 | self.assertEqual(successor(Node(22, Node(11), Node(33)).right), None) 37 | 38 | if __name__ == "__main__": 39 | unittest.main() 40 | 41 | -------------------------------------------------------------------------------- /Ch4. Trees-and-Graphs/07-build-order.py: -------------------------------------------------------------------------------- 1 | # Determine a build order given a list of projects and their dependencies. 2 | 3 | def build_order(vertices, edges): 4 | nodes = {} 5 | buildOrder = [] 6 | for vertice in vertices: 7 | nodes[vertice] = GraphNode(vertice) 8 | for edge in edges: 9 | nodes[edge[0]].add_edge(nodes[edge[1]]) 10 | queue = Queue() 11 | for vertice in vertices: 12 | node = nodes[vertice] 13 | if not node.dependencies_left: 14 | queue.add(node) 15 | while queue.is_not_empty(): 16 | node = queue.remove() 17 | buildOrder.append(node.data) 18 | for n in node.edges: 19 | n.dependencies_left -=1 20 | if not n.dependencies_left: 21 | queue.add(n) 22 | if len(buildOrder) < len(vertices): 23 | return Exception("Cycle detected") 24 | return buildOrder 25 | 26 | class GraphNode(): 27 | def __init__(self, data): 28 | self.data = data 29 | self.edges = [] 30 | self.dependencies_left = 0 31 | 32 | def add_edge(self, node): 33 | self.edges.append(node) 34 | node.dependencies_left += 1 35 | 36 | class Queue(): 37 | def __init__(self): self.array = [] 38 | def add(self, item): self.array.append(item) 39 | def remove(self): return self.array.pop(0) 40 | def is_not_empty(self): return len(self.array) > 0 41 | 42 | import unittest 43 | 44 | class Test(unittest.TestCase): 45 | def test_build_order(self): 46 | projects = ["A", "B", "C", "D", "E", "F", "G"] 47 | dependencies1 = [("C", "A"), ("B", "A"), ("F", "A"), ("F", "B"), ("F", "C"), 48 | ("A", "E"), ("B", "E"), ("D", "G")] 49 | self.assertEqual(build_order(projects, dependencies1), 50 | ["D", "F", "G", "B", "C", "A", "E"]) 51 | dependencies2 = [("A", "B"), ("B", "C"), ("C", "D"), ("D", "A")] 52 | self.assertEqual(build_order(projects, dependencies2).__class__, Exception) 53 | dependencies3 = [("A", "B"), ("A", "C"), ("E", "A"), ("E", "B"), ("A", "F"), 54 | ("B", "F"), ("C", "F"), ("G", "D")] 55 | self.assertEqual(build_order(projects, dependencies3), 56 | ["E", "G", "A", "D", "B", "C", "F"]) 57 | 58 | if __name__ == "__main__": 59 | unittest.main() 60 | 61 | -------------------------------------------------------------------------------- /Ch4. Trees-and-Graphs/08-first-ancestor.py: -------------------------------------------------------------------------------- 1 | # Find the first common ancestor of two nodes in a tree. 2 | 3 | def first_common_ancestor(node1, node2): 4 | search1, search2 = node1, node2 5 | ancestors1, ancestors2 = {}, {} 6 | while search1 or search2: 7 | if search1: 8 | if search1 in ancestors2: 9 | return search1 10 | ancestors1[search1] = True 11 | search1 = search1.parent 12 | if search2: 13 | if search2 in ancestors1: 14 | return search2 15 | ancestors2[search2] = True 16 | search2 = search2.parent 17 | return None 18 | 19 | class Node(): 20 | def __init__(self, data=None, left=None, right=None): 21 | self.data, self.left, self.right = data, left, right 22 | self.parent = None 23 | if self.left: 24 | self.left.parent = self 25 | if self.right: 26 | self.right.parent = self 27 | 28 | import unittest 29 | 30 | class Test(unittest.TestCase): 31 | def test_first_common_ancestor(self): 32 | node1 = Node(11, Node(55), Node(77, Node(44))) 33 | node2 = Node(22, Node(99)) 34 | self.assertEqual(first_common_ancestor(node1, node2), None) 35 | node3 = Node(33, node1, Node(88, Node(123, None, node2))) 36 | node4 = Node(44, node3, Node(66)) 37 | self.assertEqual(first_common_ancestor(node1, node2), node3) 38 | 39 | if __name__ == "__main__": 40 | unittest.main() 41 | -------------------------------------------------------------------------------- /Ch4. Trees-and-Graphs/09-bst-sequences.py: -------------------------------------------------------------------------------- 1 | # Enumerate all inserrtion sequences that could have led to the given BST. 2 | 3 | def bst_sequences(bst): 4 | return bst_sequences_partial([], [bst]) 5 | 6 | def bst_sequences_partial(partial, subtrees): 7 | if not subtrees: 8 | return [partial] 9 | sequence = [] 10 | for i, node in enumerate(subtrees): 11 | nextPartial = partial+[node.data] 12 | nextSubtree = subtrees[:i] + subtrees[i+1:] 13 | if node.left: 14 | nextSubtree.append(node.left) 15 | if node.right: 16 | nextSubtree.append(node.right) 17 | sequence += bst_sequences_partial(nextPartial, nextSubtree) 18 | return sequence 19 | 20 | class Node(): 21 | def __init__(self, data=None, left=None, right=None): 22 | self.data, self.left, self.right = data, left, right 23 | 24 | import unittest 25 | 26 | class Test(unittest.TestCase): 27 | def test_bst_sequences(self): 28 | self.assertEqual(bst_sequences(Node(7,Node(4,Node(5)),Node(9))), [ 29 | [7, 4, 9, 5], 30 | [7, 4, 5, 9], 31 | [7, 9, 4, 5]]) 32 | self.assertEqual(bst_sequences(Node(7,Node(4,Node(5),Node(6)),Node(9))), [ 33 | [7, 4, 9, 5, 6], 34 | [7, 4, 9, 6, 5], 35 | [7, 4, 5, 9, 6], 36 | [7, 4, 5, 6, 9], 37 | [7, 4, 6, 9, 5], 38 | [7, 4, 6, 5, 9], 39 | [7, 9, 4, 5, 6], 40 | [7, 9, 4, 6, 5]]) 41 | 42 | 43 | if __name__ == "__main__": 44 | unittest.main() 45 | 46 | -------------------------------------------------------------------------------- /Ch4. Trees-and-Graphs/10-is-subtree.py: -------------------------------------------------------------------------------- 1 | # Determine whether one binary tree is a subtree of another. 2 | 3 | def is_subtree(bt1, bt2): 4 | for node in tree_generator(bt1): 5 | if equivalent_trees(node, bt2): 6 | return True 7 | return False 8 | 9 | def equivalent_trees(bt1, bt2): 10 | if not bt1: 11 | return not bt2 12 | if not bt2: 13 | return False 14 | if bt1.data !=bt2.data: 15 | return False 16 | return equivalent_trees(bt1.left,bt2.left) and equivalent_trees(bt1.right, bt2.right) 17 | 18 | class Node(): 19 | def __init__(self, data=None, left=None, right=None): 20 | self.data, self.left, self.right = data, left, right 21 | 22 | def tree_generator(node): 23 | if not node: return 24 | yield node 25 | for child in tree_generator(node.left): yield child 26 | for child in tree_generator(node.right): yield child 27 | 28 | import unittest 29 | 30 | class Test(unittest.TestCase): 31 | def test_is_subtree(self): 32 | tree1 = Node(5,Node(3,Node(2),Node(4)),Node(8,Node(7,Node(9)),Node(1))) 33 | tree2 = Node(8,Node(7),Node(1)) 34 | self.assertEqual(is_subtree(tree1, tree2), False) 35 | tree3 = Node(8,Node(7,Node(9)),Node(1)) 36 | self.assertEqual(is_subtree(tree1, tree3), True) 37 | 38 | if __name__ == "__main__": 39 | unittest.main() 40 | 41 | -------------------------------------------------------------------------------- /Ch4. Trees-and-Graphs/11-random-node.py: -------------------------------------------------------------------------------- 1 | # Design a binary tree class that can return a random node. 2 | 3 | class Node(): 4 | def __init__(self, data=None, left=None, right=None): 5 | self.data, self.left, self.right = data, left, right 6 | self.count = 1 7 | if self.left: 8 | self.count += self.left.count 9 | if self.right: 10 | self.count += self.right.count 11 | 12 | def get_random_node(self): 13 | return self.get_numbered_node(randint(0, self.count - 1)) 14 | 15 | def get_all_nodes(self, node): 16 | if not node: return None 17 | array = [node] 18 | if node.left: 19 | array+=self.get_all_nodes(node.left) 20 | if node.right: 21 | array+=self.get_all_nodes(node.right) 22 | return array 23 | 24 | def get_numbered_node(self, number): 25 | return self.get_all_nodes(self)[number] 26 | 27 | import random 28 | import unittest 29 | 30 | mock_random_value = False 31 | 32 | def randint(lower_bound, upper_bound): 33 | if not mock_random_value is False: 34 | return mock_random_value 35 | return random.randint(lower_bound, upper_bound) 36 | 37 | class Test(unittest.TestCase): 38 | def test_mock_randint(self): 39 | global mock_random_value 40 | mock_random_value = 12 41 | self.assertEqual(randint(0, 2000), 12) 42 | 43 | def test_get_random_value(self): 44 | global mock_random_value 45 | tree = Node(11,Node(21,Node(31),Node(32,Node(41),Node(42,None,Node(51)))), 46 | Node(22,Node(33),Node(34))) 47 | mock_random_value = 0 48 | self.assertEqual(tree.get_random_node().data, 11) 49 | mock_random_value = 4 50 | self.assertEqual(tree.get_random_node().data, 41) 51 | mock_random_value = 8 52 | self.assertEqual(tree.get_random_node().data, 33) 53 | 54 | if __name__ == "__main__": 55 | unittest.main() 56 | 57 | -------------------------------------------------------------------------------- /Ch4. Trees-and-Graphs/12-paths-with-sum.py: -------------------------------------------------------------------------------- 1 | # Return all downward paths through a tree whose nodes sum to a target value. 2 | 3 | def paths_with_sum(binary_tree, target_sum): 4 | partial_paths = ListDict({target_sum: [[]]}) 5 | return paths_with_partial_sum(binary_tree, target_sum, partial_paths) 6 | 7 | def paths_with_partial_sum(node, target_sum, partial_paths): 8 | if not node: return [] 9 | result = [] 10 | nextPartial = ListDict({target_sum:[[]]}) 11 | for sums, paths in partial_paths.items(): 12 | for path in paths: 13 | nextPartial[sums-node.value] = [path+[node.name]] 14 | result = nextPartial[0] 15 | for child in [node.left, node.right]: 16 | result += (paths_with_partial_sum(child, target_sum, nextPartial)) 17 | return result 18 | 19 | class Node(): 20 | def __init__(self, name, value, left=None, right=None): 21 | self.name, self.value, self.left, self.right = name, value, left, right 22 | 23 | class ListDict(dict): 24 | def __missing__(self, key): 25 | return [] 26 | 27 | import unittest 28 | 29 | class Test(unittest.TestCase): 30 | def test_paths_with_sum(self): 31 | bt=Node("A",4,Node("B",-2,Node("D",7),Node("E", 4)), 32 | Node("C", 7,Node("F",-1,Node("H",-1),Node("I",2,Node("K",1))), 33 | Node("G", 0,None, Node("J", -2)))) 34 | self.assertEqual(paths_with_sum(bt, 2), [["A", "B"], ["B", "E"], ["I"], 35 | ["F", "I", "K"]]) 36 | self.assertEqual(paths_with_sum(bt, 12), [["A", "C", "F", "I"]]) 37 | self.assertEqual(paths_with_sum(bt, 9), [["A","B","D"], ["A","C","F","H"], 38 | ["C","F","I","K"], ["A","C","G","J"]]) 39 | 40 | if __name__ == "__main__": 41 | unittest.main() 42 | 43 | -------------------------------------------------------------------------------- /Ch5. Bit-manipulation/01-insertion.py: -------------------------------------------------------------------------------- 1 | # Insert `m` into `n` between `i` and `j`. 2 | 3 | def insertion(n, m, i, j): 4 | cleared_n = n & ~((1 << (j+1)) - (1 << i)) 5 | shifted_m = m << i 6 | return cleared_n | shifted_m 7 | 8 | import unittest 9 | 10 | class Test(unittest.TestCase): 11 | def test_insertion(self): 12 | self.assertEqual(insertion(0b11111111, 0b10, 2, 5), 0b11001011) 13 | self.assertEqual(insertion(0b00000000, 0b1010, 4, 7), 0b10100000) 14 | 15 | if __name__ == "__main__": 16 | unittest.main() 17 | 18 | -------------------------------------------------------------------------------- /Ch5. Bit-manipulation/02-binary-to-string.py: -------------------------------------------------------------------------------- 1 | # Return a binary string representation of the given floating point number 2 | # between 0 and 1 as long as it accurately fits in 32 bits. 3 | 4 | import ctypes 5 | 6 | def binary_to_string(number): 7 | if number == 1: 8 | return "1.0" 9 | if number == 0: 10 | return "0.0" 11 | if number < 0 or number > 1: 12 | return Exception("Out of bounds") 13 | normalized = bits_for(number + 1.0) 14 | bits = normalized & ((1 << 52) - 1) 15 | if normalized & ((1 << 22) - 1): 16 | return Exception("Insufficient precision") 17 | digits = ["0."] 18 | bit = (1 << 51) 19 | while bits: 20 | if bits & bit: 21 | digits.append("1") 22 | else: 23 | digits.append("0") 24 | bits &= ~bit 25 | bit >>= 1 26 | return "".join(digits) 27 | 28 | def bits_for(number): 29 | return ctypes.c_longlong.from_buffer(ctypes.c_double(number)).value 30 | 31 | import unittest 32 | 33 | class Test(unittest.TestCase): 34 | def test_binary_to_string(self): 35 | self.assertEqual(binary_to_string(0.75), "0.11") 36 | self.assertEqual(binary_to_string(0.625), "0.101") 37 | self.assertEqual(binary_to_string(0.3).message, "Insufficient precision") 38 | 39 | if __name__ == "__main__": 40 | unittest.main() 41 | 42 | -------------------------------------------------------------------------------- /Ch5. Bit-manipulation/03-flip-bit-to-win.py: -------------------------------------------------------------------------------- 1 | # Determine the longest sequence of 1s attainable by flipping up to one digit. 2 | 3 | def longest_sequence_after_flip(bits): 4 | bit = 1 << 63 5 | longest = 0 6 | current_without_flip = 0 7 | current_with_flip = 0 8 | while bit: 9 | if bits & bit: 10 | current_without_flip += 1 11 | current_with_flip += 1 12 | else: 13 | current_with_flip = current_without_flip + 1 14 | current_without_flip = 0 15 | if current_with_flip > longest: 16 | longest = current_with_flip 17 | bit >>= 1 18 | return longest 19 | 20 | import unittest 21 | 22 | class Test(unittest.TestCase): 23 | def test_longest_sequence_after_flip(self): 24 | self.assertEqual(longest_sequence_after_flip(0b1111100), 6) 25 | self.assertEqual(longest_sequence_after_flip(0b0111111), 7) 26 | self.assertEqual(longest_sequence_after_flip(-1), 64) 27 | self.assertEqual(longest_sequence_after_flip(0b1011110111001111110), 8) 28 | 29 | if __name__ == "__main__": 30 | unittest.main() 31 | 32 | -------------------------------------------------------------------------------- /Ch5. Bit-manipulation/04-next-number.py: -------------------------------------------------------------------------------- 1 | # Return the next larger and next smaller numbers that use the same number of 2 | # 1's in their binary representation as the given positive integer. 3 | 4 | def next_numbers(number): 5 | if number % 2: 6 | # Larger X011...1 --> X101...1 7 | negated = ~number 8 | lsz = negated & -negated 9 | larger = number ^ (lsz | (lsz >> 1)) 10 | # Smaller X100...01...1 --> X011...10...0 11 | end_ones = lsz - 1 12 | cleared = number ^ end_ones 13 | if cleared: 14 | next_bit = cleared & -cleared 15 | flip_bits1 = next_bit | (next_bit >> 1) 16 | extra_zeros = ((next_bit >> 1) - 1) ^ end_ones 17 | flip_bits2 = extra_zeros | (extra_zeros >> end_ones.bit_length()) 18 | smaller = number ^ flip_bits1 ^ flip_bits2 19 | else: 20 | smaller = None 21 | else: 22 | # Smaller X100...0 --> X010...0 23 | lsb = number & -number 24 | smaller = number ^ (lsb | (lsb >> 1)) 25 | # Larger X011...10...0 --> X100...01...1 26 | end_zeros = lsb - 1 27 | cleared_negated = ~number ^ end_zeros 28 | next_zero = cleared_negated & -cleared_negated 29 | flip_bits1 = next_zero | (next_zero >> 1) 30 | extra_ones = ((next_zero >> 1) - 1) ^ end_zeros 31 | flip_bits2 = extra_ones | (extra_ones >> end_zeros.bit_length()) 32 | larger = number ^ flip_bits1 ^ flip_bits2 33 | return (smaller, larger) 34 | 35 | import unittest 36 | 37 | class Test(unittest.TestCase): 38 | def test_next_numbers(self): 39 | self.assertEqual(next_numbers(8), (4, 16)) 40 | self.assertEqual(next_numbers(12), (10, 17)) 41 | self.assertEqual(next_numbers(15), (None, 23)) 42 | self.assertEqual(next_numbers(143), (124, 151)) 43 | self.assertEqual(next_numbers(159), (126, 175)) 44 | 45 | if __name__ == "__main__": 46 | unittest.main() 47 | 48 | -------------------------------------------------------------------------------- /Ch5. Bit-manipulation/05-debugger.py: -------------------------------------------------------------------------------- 1 | # Determine what the following function does. 2 | 3 | def is_power_of_two_or_zero(n): 4 | return (n & (n-1)) == 0 5 | 6 | import unittest 7 | 8 | class Test(unittest.TestCase): 9 | def test_is_power_of_two(self): 10 | self.assertTrue( is_power_of_two_or_zero(0)) 11 | self.assertTrue( is_power_of_two_or_zero(1)) 12 | self.assertTrue( is_power_of_two_or_zero(2)) 13 | self.assertFalse(is_power_of_two_or_zero(3)) 14 | self.assertTrue( is_power_of_two_or_zero(4)) 15 | self.assertFalse(is_power_of_two_or_zero(5)) 16 | self.assertFalse(is_power_of_two_or_zero(6)) 17 | self.assertFalse(is_power_of_two_or_zero(1000)) 18 | self.assertTrue( is_power_of_two_or_zero(1024)) 19 | 20 | if __name__ == "__main__": 21 | unittest.main() 22 | -------------------------------------------------------------------------------- /Ch5. Bit-manipulation/06-conversion.py: -------------------------------------------------------------------------------- 1 | # Tell how many bits must be flipped in order to transform one number into 2 | # the other. 3 | 4 | EVEN = 0x5555555555555555 5 | ODD = 0xAAAAAAAAAAAAAAAA 6 | 7 | # This is better for numbers that are nearly the same. 8 | def bits_different_1(a, b): 9 | different = a ^ b 10 | count = 0 11 | while different: 12 | different ^= different & -different 13 | count += 1 14 | return count 15 | 16 | # This is better for numbers that differ by a lot. 17 | def bits_different_2(a, b): 18 | different = a ^ b 19 | counts = (different & EVEN) + ((different & ODD) >> 1) 20 | counts = (counts & 0x3333333333333333) + ((counts & 0xCCCCCCCCCCCCCCCC) >> 2) 21 | counts = (counts & 0x0F0F0F0F0F0F0F0F) + ((counts & 0xF0F0F0F0F0F0F0F0) >> 4) 22 | counts = (counts & 0x00FF00FF00FF00FF) + ((counts & 0xFF00FF00FF00FF00) >> 8) 23 | counts = (counts & 0x0000FFFF0000FFFF) + ((counts & 0xFFFF0000FFFF0000) >> 16) 24 | counts = (counts & 0x00000000FFFFFFFF) + (counts >> 32) 25 | return counts 26 | 27 | import unittest 28 | 29 | class Test(unittest.TestCase): 30 | def test_bits_different_1(self): 31 | self.assertEqual(bits_different_1(16, 2), 2) 32 | self.assertEqual(bits_different_1(17, 34), 4) 33 | self.assertEqual(bits_different_1(15, 97), 5) 34 | 35 | def test_bits_different_2(self): 36 | self.assertEqual(bits_different_2(16, 2), 2) 37 | self.assertEqual(bits_different_2(17, 34), 4) 38 | self.assertEqual(bits_different_2(15, 97), 5) 39 | 40 | if __name__ == "__main__": 41 | unittest.main() 42 | 43 | -------------------------------------------------------------------------------- /Ch5. Bit-manipulation/07-pairwise-swap.py: -------------------------------------------------------------------------------- 1 | # Swap the odd and even bits in a number. 2 | 3 | EVEN = 0x5555555555555555 4 | ODD = 0xAAAAAAAAAAAAAAAA 5 | 6 | def swap_odd_even_bits(n): 7 | return ((n & ODD) >> 1) | ((n & EVEN) << 1) 8 | 9 | import unittest 10 | 11 | class Test(unittest.TestCase): 12 | def test_swap_odd_even_bits(self): 13 | self.assertEqual(swap_odd_even_bits(42), 21) 14 | self.assertEqual(swap_odd_even_bits(21), 42) 15 | self.assertEqual(swap_odd_even_bits(43), 23) 16 | self.assertEqual(swap_odd_even_bits(EVEN), ODD) 17 | self.assertEqual(swap_odd_even_bits(511), 767) 18 | self.assertEqual(swap_odd_even_bits(1023), 1023) 19 | 20 | if __name__ == "__main__": 21 | unittest.main() 22 | -------------------------------------------------------------------------------- /Ch5. Bit-manipulation/08-draw-line.py: -------------------------------------------------------------------------------- 1 | # Draw a horizontal line from (x_1, y) to (x_2, y). 2 | 3 | def draw_line(screen, width, x1, x2, y): 4 | byte_width = width / 8 5 | height = len(screen) / byte_width 6 | if x1 < x2: 7 | x_start, x_end = x1, x2 8 | else: 9 | x_start, x_end = x2, x1 10 | if x_start < 0 or x_end > width or y > height: 11 | return None 12 | byte = y * byte_width + x_start / 8 13 | byte_end = y * byte_width + x_end / 8 14 | screen[byte] = (1 << (x_start % 8)) - 1 15 | byte += 1 16 | while byte < byte_end: 17 | screen[byte] = 255 18 | byte += 1 19 | screen[byte] = 255 ^ ((1 << (x_end % 8)) - 1) 20 | 21 | import unittest 22 | 23 | class Test(unittest.TestCase): 24 | def test_draw_line(self): 25 | screen = [0, 0, 0, 0, 0, 0, 0, 0, 26 | 0, 0, 0, 0, 0, 0, 0, 0, 27 | 0, 0, 0, 0, 0, 0, 0, 0] 28 | draw_line(screen, 64, 20, 42, 1) 29 | self.assertEqual(screen, [0]*8 + [0, 0, 15, 255, 255, 252, 0, 0] + [0]*8) 30 | 31 | if __name__ == "__main__": 32 | unittest.main() 33 | 34 | -------------------------------------------------------------------------------- /Ch7. OOD/01-deck-of-cards.py: -------------------------------------------------------------------------------- 1 | # Design a deck of cards. 2 | 3 | class CardDeck(): 4 | def __init__(self, cards): 5 | if cards: 6 | self.cards = cards 7 | else: 8 | self.cards = [] 9 | 10 | def shuffle(self): 11 | for i in xrange(len(self.cards)): 12 | o = random.randint(i) 13 | self.cards[i], self.cards[o] = self.cards[o], self.cards[i] 14 | 15 | def draw_card(self): 16 | return self.cards.pop() 17 | 18 | class BlackjackHand(CardDeck): 19 | def value(self): 20 | value, aces = 0, 0 21 | for card in self.cards: 22 | value += min(card.number, 10) 23 | aces += 1 24 | while value <= 11: 25 | if aces: 26 | value += 10 27 | aces -= 1 28 | return value 29 | 30 | class Card(): 31 | def __init__(self, number, suit): 32 | self.number, self.suit = number, suit 33 | 34 | import unittest 35 | 36 | class Test(unittest.TestCase): 37 | def test_card_deck(self): 38 | deck = CardDeck([Card(2, "Hearts"), Card(4, "Clubs")]) 39 | self.assertEqual(deck.draw_card().suit, "Clubs") 40 | 41 | def test_blackjack_hand(self): 42 | hand = BlackjackHand([Card(5,"Diamonds"), Card(7,"Clubs")]) 43 | self.assertEqual(hand.value(), 12) 44 | hand = BlackjackHand([Card(5,"Diamonds"), Card(1,"Clubs")]) 45 | self.assertEqual(hand.value(), 16) 46 | hand = BlackjackHand([Card(12,"Diamonds"),Card(1,"Clubs")]) 47 | self.assertEqual(hand.value(), 21) 48 | hand = BlackjackHand([Card(12,"Diamonds"),Card(1,"Clubs"),Card(1,"Hearts")]) 49 | self.assertEqual(hand.value(), 12) 50 | 51 | if __name__ == "__main__": 52 | unittest.main() 53 | 54 | -------------------------------------------------------------------------------- /Ch7. OOD/02-call-center.py: -------------------------------------------------------------------------------- 1 | # Design a call center. 2 | 3 | class CallCenter(): 4 | def __init__(self, respondents, managers, director): 5 | self.respondents = respondents 6 | self.managers = managers 7 | self.director = director 8 | self.respondent_queue = [] 9 | self.call_queue = [] 10 | for respondent in respondents: 11 | respondent.callcenter = self 12 | if not respondent.call: 13 | self.respondent_queue.append(respondent) 14 | 15 | def route_respondent(self, respondent): 16 | if len(self.call_queue): 17 | respondent.take_call(self.call_queue.pop(0)) 18 | else: 19 | self.respondent_queue.append(respondent) 20 | 21 | def route_call(self, call): 22 | if len(self.respondent_queue): 23 | self.respondent_queue.pop(0).take_call(call) 24 | else: 25 | self.call_queue.append(call) 26 | 27 | class Call(): 28 | def __init__(self, issue): 29 | self.issue = issue 30 | self.employee = None 31 | 32 | def resolve(self, handled): 33 | if handled: 34 | self.issue = None 35 | self.employee.finish_call(handled) 36 | 37 | def apologize_and_hangup(self): 38 | # "Try calling our competitor." 39 | self.employee = None 40 | 41 | class Employee(object): 42 | def __init__(self, name, manager): 43 | self.name, self.manager = name, manager 44 | self.call = None 45 | 46 | def take_call(self, call): 47 | if self.call: 48 | self.escalate(call) 49 | else: 50 | self.call = call 51 | self.call.employee = self 52 | 53 | def escalate(self, call): 54 | if self.manager: 55 | self.manager.take_call(call) 56 | else: 57 | call.apologize_and_hangup() 58 | 59 | def finish_call(self, handled=True): 60 | if not handled: 61 | if self.manager: 62 | self.manager.take_call(self.call) 63 | else: 64 | call.apologize_and_hangup() 65 | self.call = None 66 | 67 | class Respondent(Employee): 68 | def finish_call(self, handled=True): 69 | super(Respondent, self).finish_call(handled) 70 | self.callcenter.route_respondent(self) 71 | 72 | class Manager(Employee): 73 | pass 74 | 75 | class Director(Employee): 76 | def __init__(self, name): 77 | super(Director, self).__init__(name, None) 78 | 79 | import unittest 80 | 81 | class Test(unittest.TestCase): 82 | def test_call_center(self): 83 | lashaun = Director("Lashaun") 84 | juan = Manager("Juan", lashaun) 85 | sally = Manager("Sally", lashaun) 86 | boris = Respondent("Boris", juan) 87 | sam = Respondent("Sam", juan) 88 | jordan = Respondent("Jordan", sally) 89 | casey = Respondent("Casey", sally) 90 | center = CallCenter([boris, sam, jordan, casey], [juan, lashaun], sally) 91 | call1 = Call("The toilet") 92 | call2 = Call("The webpage") 93 | call3 = Call("The email") 94 | call4 = Call("The lizard") 95 | call5 = Call("The cloudy weather") 96 | call6 = Call("The noise") 97 | self.assertEqual(len(center.respondent_queue), 4) 98 | center.route_call(call1) 99 | center.route_call(call2) 100 | self.assertEqual(len(center.respondent_queue), 2) 101 | center.route_call(call3) 102 | center.route_call(call4) 103 | center.route_call(call5) 104 | center.route_call(call6) 105 | self.assertEqual(center.call_queue, [call5, call6]) 106 | call1.resolve(True) 107 | self.assertEqual(call1.issue, None) 108 | self.assertEqual(center.call_queue, [call6]) 109 | self.assertEqual(sally.call, None) 110 | self.assertEqual(lashaun.call, None) 111 | call4.resolve(False) 112 | self.assertEqual(sally.call, call4) 113 | call4.resolve(False) 114 | self.assertEqual(sally.call, None) 115 | self.assertEqual(lashaun.call, call4) 116 | call4.resolve(True) 117 | self.assertEqual(lashaun.call, None) 118 | call6.resolve(True) 119 | self.assertEqual(center.respondent_queue, [casey]) 120 | 121 | if __name__ == "__main__": 122 | unittest.main() 123 | 124 | -------------------------------------------------------------------------------- /Ch7. OOD/03-jukebox.py: -------------------------------------------------------------------------------- 1 | # Design a jukebox. 2 | 3 | class Jukebox(object): 4 | def __init__(self, songs): 5 | self.songs = {} 6 | for song in songs: 7 | self.songs[song.title] = song 8 | self.playing = None 9 | 10 | def play_song(self, title): 11 | if self.playing: 12 | self.stop_song() 13 | self.playing = self.songs[title] 14 | self.playing.play() 15 | 16 | def stop_song(self): 17 | if self.playing: 18 | self.playing.stop() 19 | 20 | class Song(object): 21 | def __init__(self, title, data): 22 | self.title, self.data = title, data 23 | self.play_count = 0 24 | 25 | def play(self): 26 | self.is_playing = True 27 | self.play_count += 1 28 | 29 | def stop(self): 30 | self.is_playing = False 31 | 32 | import unittest 33 | 34 | class Test(unittest.TestCase): 35 | def test_jukebox(self): 36 | song1 = Song("Just Dance", "8598742398723498") 37 | song2 = Song("When You Wish Upon a Beard", "9879834759827209384") 38 | jukebox = Jukebox([song1, song2]) 39 | jukebox.play_song("When You Wish Upon a Beard") 40 | self.assertEqual(song2.play_count, 1) 41 | 42 | if __name__ == "__main__": 43 | unittest.main() 44 | 45 | -------------------------------------------------------------------------------- /Ch7. OOD/04-parking-lot.py: -------------------------------------------------------------------------------- 1 | # Design a parking lot. 2 | 3 | class ParkingLot(object): 4 | def __init__(self): 5 | self.cars = {} 6 | 7 | def park_car(self, car): 8 | self.cars[car] = True 9 | 10 | def unpark_car(self, car): 11 | del self.cars[car] 12 | 13 | class Car(object): pass 14 | 15 | import unittest 16 | 17 | class Test(unittest.TestCase): 18 | def test_parking_lot(self): 19 | lot = ParkingLot() 20 | car1 = Car() 21 | car2 = Car() 22 | lot.park_car(car1) 23 | lot.park_car(car2) 24 | lot.unpark_car(car1) 25 | self.assertEqual(len(lot.cars), 1) 26 | 27 | if __name__ == "__main__": 28 | unittest.main() 29 | 30 | -------------------------------------------------------------------------------- /Ch7. OOD/05-online-book-reader.py: -------------------------------------------------------------------------------- 1 | # Design an online book reader. 2 | 3 | class BookReader(object): 4 | def __init__(self, books): 5 | self.books = books 6 | 7 | def read_next_book(self): 8 | if len(self.books): 9 | return self.books.pop(0).text 10 | else: 11 | return None 12 | 13 | class Book(object): 14 | def __init__(self, text): 15 | self.text = text 16 | 17 | import unittest 18 | 19 | class Test(unittest.TestCase): 20 | def test_book_reader(self): 21 | reader = BookReader([Book("The start."), Book("The end.")]) 22 | self.assertEqual(reader.read_next_book(), "The start.") 23 | self.assertEqual(reader.read_next_book(), "The end.") 24 | self.assertEqual(reader.read_next_book(), None) 25 | 26 | if __name__ == "__main__": 27 | unittest.main() 28 | 29 | -------------------------------------------------------------------------------- /Ch7. OOD/06-jigsaw.py: -------------------------------------------------------------------------------- 1 | # Design a jigsaw puzzle. 2 | 3 | import random 4 | 5 | class Puzzle(object): 6 | def __init__(self, n): 7 | pieces = [[Piece() for r in xrange(n)] for c in xrange(n)] 8 | for r in xrange(n): 9 | for c in xrange(n): 10 | if r: 11 | pieces[r][c].fit_with(pieces[r-1][c]) 12 | if c: 13 | pieces[r][c].fit_with(pieces[r][c-1]) 14 | self.pieces = sum(pieces, []) 15 | for i, _ in enumerate(self.pieces): 16 | j = random.randint(0, len(pieces)-1) 17 | self.pieces[i], self.pieces[j] = self.pieces[j], self.pieces[i] 18 | 19 | def is_solved(self): 20 | for piece in self.pieces: 21 | if piece.connected != piece.fits: 22 | return False 23 | return True 24 | 25 | def solve(puzzle): 26 | for piece in puzzle.pieces: 27 | for other in puzzle.pieces: 28 | if other in piece.fits: 29 | piece.connect(other) 30 | 31 | class Piece(object): 32 | def __init__(self): 33 | self.fits = set() 34 | self.connected = set() 35 | 36 | def fit_with(self, other): 37 | self.fits.add(other) 38 | other.fits.add(self) 39 | 40 | def connect(self, other): 41 | self.connected.add(other) 42 | other.connected.add(self) 43 | 44 | import unittest 45 | 46 | class Test(unittest.TestCase): 47 | def test_puzzle(self): 48 | puzzle = Puzzle(20) 49 | self.assertEqual(puzzle.is_solved(), False) 50 | solve(puzzle) 51 | self.assertEqual(puzzle.is_solved(), True) 52 | 53 | if __name__ == "__main__": 54 | unittest.main() 55 | 56 | -------------------------------------------------------------------------------- /Ch7. OOD/07-chat-server.py: -------------------------------------------------------------------------------- 1 | # Design a chat server. 2 | 3 | class ChatServer(object): 4 | def __init__(self): 5 | self.participants = set() 6 | self.messages = [] 7 | 8 | def join(self, participant): 9 | self.participants.add(participant) 10 | for message in self.messages[-8:]: 11 | participant.send(message) 12 | 13 | def leave(self, participant): 14 | if participant in self.participants: 15 | self.participants.remove(participant) 16 | participant.clear_history() 17 | 18 | def send_all(self, participant, text): 19 | message = (participant.name, text) 20 | self.messages.append(message) 21 | for p in self.participants: 22 | p.send(message) 23 | 24 | class Participant(object): 25 | def __init__(self, name): 26 | self.name = name 27 | self.history = [] 28 | 29 | def send(self, message): 30 | self.history.append(message) 31 | 32 | def clear_history(self): 33 | self.history = [] 34 | 35 | import unittest 36 | 37 | class Test(unittest.TestCase): 38 | def test_chat_server(self): 39 | server = ChatServer() 40 | albert = Participant("Albert") 41 | jordi = Participant("Jordi") 42 | martha = Participant("Martha") 43 | kat = Participant("Kat") 44 | self.assertEqual(server.participants, set()) 45 | server.join(albert) 46 | for i in xrange(12): 47 | server.send_all(albert, i) 48 | server.join(jordi) 49 | server.leave(jordi) 50 | server.join(martha) 51 | self.assertEqual(server.participants, {albert, martha}) 52 | self.assertEqual(albert.history, [('Albert', i) for i in xrange(12)]) 53 | self.assertEqual(martha.history, [('Albert', i) for i in xrange(4, 12)]) 54 | self.assertEqual(jordi.history, []) 55 | server.send_all(martha, "AlphaGo's victory was surprising!") 56 | server.join(kat) 57 | server.send_all(kat, "It's too bad Arimaa didn't outlast Go.") 58 | self.assertEqual(kat.history[-1], albert.history[-1]) 59 | 60 | if __name__ == "__main__": 61 | unittest.main() 62 | 63 | -------------------------------------------------------------------------------- /Ch7. OOD/08-othello.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Design an Othello game. 4 | 5 | import random 6 | 7 | MOVE_DOTS = True 8 | WHITE_SEARCH_DEPTH = 6 # None for human 9 | BLACK_SEARCH_DEPTH = None # None for human 10 | 11 | DIRECTIONS = [(1, 0xFEFEFEFEFEFEFEFE), (-1, 0x7F7F7F7F7F7F7F7F), 12 | (8, 0xFFFFFFFFFFFFFFFF), (-8, 0xFFFFFFFFFFFFFFFF), 13 | (7, 0x7F7F7F7F7F7F7F7F), (-7, 0xFEFEFEFEFEFEFEFE), 14 | (9, 0xFEFEFEFEFEFEFEFE), (-9, 0x7F7F7F7F7F7F7F7F)] 15 | 16 | EDGE = 0x7E8181818181817E 17 | CORNER = 0x8100000000000081 18 | NOT_CN = -0x42c300000000c343 19 | BLACK_START = (1<<27) | (1<<36) 20 | WHITE_START = (1<<28) | (1<<35) 21 | 22 | class Othello(object): 23 | def __init__(self, side=0, black=BLACK_START, white=WHITE_START): 24 | self.side = side 25 | self.bits = [black, white] 26 | 27 | def end_score(self): 28 | score = popcount(self.bits[self.side]) - popcount(self.bits[self.side^1]) 29 | return score * 1024 30 | 31 | def evaluate(self): 32 | friendly, enemy = self.bits[self.side], self.bits[self.side^1] 33 | empty_n = neighbors(~(friendly | enemy)) 34 | value = float(popcount(friendly & NOT_CN) - popcount(enemy & NOT_CN)) 35 | value += 0.875 * float(count(friendly & EDGE) - count(enemy & EDGE)) 36 | value += 8 * float(count(friendly & CORNER) - count(enemy & CORNER)) 37 | value -= 1.5 * (popcount(friendly & empty_n) - popcount(enemy & empty_n)) 38 | return value + random.random() 39 | 40 | def legal_moves(self): 41 | moves = [] 42 | friendly, enemy = self.bits[self.side], self.bits[self.side^1] 43 | placement = self.bits[0] | self.bits[1] 44 | potential = neighbors(enemy) & ~placement 45 | while potential: 46 | move = potential & -potential 47 | potential ^= move 48 | for step, mask in DIRECTIONS: 49 | bit = shift(move, step) & mask & enemy 50 | if bit == 0: 51 | continue 52 | bit = shift(bit, step) & mask 53 | while bit & enemy: 54 | bit = shift(bit, step) & mask 55 | if bit & friendly: 56 | moves.append(move) 57 | break 58 | return moves 59 | 60 | def winner(self): 61 | if not len(self.legal_moves()): 62 | black_count = popcount(self.bits[0]) 63 | white_count = popcount(self.bits[1]) 64 | if black_count == white_count: 65 | return "Tie" 66 | elif black_count > white_count: 67 | return "Black" 68 | else: 69 | return "White" 70 | return None 71 | 72 | def make_move(self, row, col): 73 | if row < 0 or row > 7 or col < 0 or col > 7: 74 | raise Exception('Invalid location.') 75 | bit = 1 << (row * 8 + col) 76 | if not bit in self.legal_moves(): 77 | raise Exception('Illegal move.') 78 | self.make_bit_move(bit) 79 | 80 | def make_bit_move(self, move): 81 | self.bits[self.side] |= move 82 | friendly, enemy = self.bits[self.side], self.bits[self.side^1] 83 | for step, mask in DIRECTIONS: 84 | bit = shift(move, step) & mask & enemy 85 | if bit == 0: 86 | continue 87 | bit = shift(bit, step) & mask 88 | while bit & enemy: 89 | bit = shift(bit, step) & mask 90 | if bit & friendly == 0: 91 | continue 92 | bit = shift(bit, -step) 93 | while bit & enemy: 94 | self.bits[0] ^= bit 95 | self.bits[1] ^= bit 96 | bit = shift(bit, -step) 97 | self.side ^= 1 98 | 99 | def negamax(self, depth): 100 | moves = self.legal_moves() 101 | if not depth: 102 | return moves[random.randint(0, len(moves) - 1)] 103 | best_move = moves[0] 104 | best_rating = float('-inf') 105 | solid_best_move = moves[0] 106 | solid_best_rating = float('-inf') 107 | for move in moves: 108 | rating = -self.ab_rate_move(move, depth, float('-inf'), -best_rating) 109 | if rating > best_rating: 110 | best_rating = rating 111 | best_move = move 112 | return best_move 113 | 114 | def ab_rate_move(self, move, depth, lower, upper): 115 | board = Othello(self.side, self.bits[0], self.bits[1]) 116 | board.make_bit_move(move) 117 | if depth == 1: 118 | return board.evaluate() 119 | moves = board.legal_moves() 120 | if len(moves) == 0: 121 | return board.end_score() 122 | best_rating = float('-inf') 123 | new_lower = lower 124 | for m in moves: 125 | rating = -board.ab_rate_move(m, depth-1, -upper, -new_lower) 126 | if rating > best_rating: 127 | best_rating = rating 128 | if best_rating > new_lower: 129 | new_lower = best_rating 130 | if new_lower > upper: 131 | break 132 | return best_rating 133 | 134 | def __str__(self): 135 | moves = self.legal_moves() 136 | parts = ['\n +-----------------+\n'] 137 | for r in xrange(8): 138 | parts.append(' ' + str(8 - r) + '| ') 139 | for c in xrange(8): 140 | bit = 1 << (r * 8 + c) 141 | if bit & self.bits[0]: parts.append('○ ') 142 | elif bit & self.bits[1]: parts.append('● ') 143 | elif MOVE_DOTS and (bit in moves): parts.append('. ') 144 | else: parts.append(' ') 145 | parts.append('|\n') 146 | parts.append(' +-----------------+\n') 147 | parts.append(' a b c d e f g h\n\n') 148 | return "".join(parts) 149 | 150 | def popcount(bits): 151 | counts = (bits & 0x5555555555555555) + ((bits & 0xAAAAAAAAAAAAAAAA) >> 1) 152 | counts = (counts & 0x3333333333333333) + ((counts & 0xCCCCCCCCCCCCCCCC) >> 2) 153 | counts = (counts & 0x0F0F0F0F0F0F0F0F) + ((counts & 0xF0F0F0F0F0F0F0F0) >> 4) 154 | counts = (counts & 0x00FF00FF00FF00FF) + ((counts & 0xFF00FF00FF00FF00) >> 8) 155 | counts = (counts & 0x0000FFFF0000FFFF) + ((counts & 0xFFFF0000FFFF0000) >> 16) 156 | counts = (counts & 0x00000000FFFFFFFF) + (counts >> 32) 157 | return counts 158 | 159 | def count(bits): 160 | count = 0 161 | while bits: 162 | bits &= bits - 1 163 | count += 1 164 | return count 165 | 166 | def neighbors(bits): 167 | nbits = (bits << 1) & 0xFEFEFEFEFEFEFEFE 168 | nbits |= (bits >> 1) & 0x7F7F7F7F7F7F7F7F 169 | nbits |= (bits << 9) & 0xFEFEFEFEFEFEFEFE 170 | nbits |= (bits << 7) & 0x7F7F7F7F7F7F7F7F 171 | nbits |= (bits >> 7) & 0xFEFEFEFEFEFEFEFE 172 | nbits |= (bits >> 9) & 0x7F7F7F7F7F7F7F7F 173 | nbits |= (bits << 8) & 0xFFFFFFFFFFFFFFFF 174 | return nbits | (bits >> 8) 175 | 176 | def shift(bits, step): 177 | if step > 0: 178 | return bits << step 179 | return bits >> -step 180 | 181 | def play_game(): 182 | board = Othello() 183 | while len(board.legal_moves()): 184 | if board.side: 185 | if not WHITE_SEARCH_DEPTH is None: 186 | print(str(board) + 'The computer is making a move for ●.') 187 | board.make_bit_move(board.negamax(WHITE_SEARCH_DEPTH)) 188 | continue 189 | elif not BLACK_SEARCH_DEPTH is None: 190 | print(str(board) + 'The computer is making a move for ○.') 191 | board.make_bit_move(board.negamax(BLACK_SEARCH_DEPTH)) 192 | continue 193 | if board.side: 194 | prompt = 'Choose where to place a ●. (ie "d3")\n> ' 195 | else: 196 | prompt = 'Choose where to place a ○. (ie "d3")\n> ' 197 | line = raw_input(str(board) + prompt) 198 | try: 199 | row = 8 - int(line[1]) 200 | col = ord(line[0]) - 97 201 | board.make_move(row, col) 202 | except Exception as e: 203 | print(e) 204 | print(str(board)) 205 | winner = board.winner() 206 | if winner == "Tie": 207 | print("The game is a tie!") 208 | elif winner == "Black": 209 | print("○ wins!") 210 | else: 211 | print("● wins!") 212 | return winner 213 | 214 | if __name__ == "__main__": 215 | import sys 216 | if sys.argv[-1] == 'play': 217 | play_game() 218 | else: 219 | import unittest 220 | class Test(unittest.TestCase): 221 | def test_othello(self): 222 | board = Othello() 223 | self.assertEqual(board.side, 0) 224 | self.assertEqual(board.end_score(), 0) 225 | self.assertEqual(len(board.legal_moves()), 4) 226 | board.make_bit_move(board.legal_moves()[0]) 227 | self.assertEqual(board.side, 1) 228 | self.assertEqual(len(board.legal_moves()), 3) 229 | board = Othello(1, 0x2040c08000000, 0x181010000000) 230 | self.assertEqual(board.negamax(1), (1 << 56)) 231 | self.assertEqual(board.negamax(2), (1 << 56)) 232 | self.assertEqual(board.negamax(3), (1 << 56)) 233 | unittest.main() 234 | 235 | -------------------------------------------------------------------------------- /Ch7. OOD/09-circular-array.py: -------------------------------------------------------------------------------- 1 | # Design an efficient circular array. 2 | 3 | class CircularArray(object): 4 | def __init__(self, array): 5 | self.array = array 6 | self.start = 0 7 | 8 | def rotate(self, i): 9 | self.start = (self.start + i) % len(self.array) 10 | 11 | def __iter__(self): 12 | for i in xrange(self.start, len(self.array)): 13 | yield self.array[i] 14 | for i in xrange(0, self.start): 15 | yield self.array[i] 16 | 17 | def __getitem__(self, i): 18 | return self.array[(self.start + i) % len(self.array)] 19 | 20 | def __setitem__(self, i, item): 21 | self.array[(self.start + i) % len(self.array)] = item 22 | 23 | def __delitem__(self, i): 24 | index = (self.start + i) % len(self.array) 25 | del self.array[index] 26 | if index < self.start: 27 | self.start -= 1 28 | 29 | import unittest 30 | 31 | class Test(unittest.TestCase): 32 | def test_circular_array(self): 33 | ca = CircularArray([11, 22, 33, 44, 55, 66, 77]) 34 | ca.rotate(5) 35 | self.assertEqual(ca[0], 66) 36 | array = [] 37 | for item in ca: 38 | array.append(item) 39 | self.assertEqual(array, [66, 77, 11, 22, 33, 44, 55]) 40 | ca[3] = 999 41 | del ca[4] 42 | array = [] 43 | for item in ca: 44 | array.append(item) 45 | self.assertEqual(array, [66, 77, 11, 999, 44, 55]) 46 | ca.rotate(2) 47 | array = [] 48 | for item in ca: 49 | array.append(item) 50 | self.assertEqual(array, [11, 999, 44, 55, 66, 77]) 51 | 52 | if __name__ == "__main__": 53 | unittest.main() 54 | 55 | -------------------------------------------------------------------------------- /Ch7. OOD/10-minesweeper.py: -------------------------------------------------------------------------------- 1 | # Design Minesweeper. 2 | 3 | class Minesweeper(object): 4 | def __init__(self, rows, cols, bombs_count=None): 5 | self.rows, self.cols = rows, cols 6 | self.game_over = False 7 | self.game_won = False 8 | self.bombs_found = 0 9 | self.bombs_count = bombs_count 10 | if not self.bombs_count: 11 | self.bombs_count = (rows * cols + 10) / 10 12 | self.bombs = [[0 for r in xrange(rows+2)] for c in xrange(cols+2)] 13 | self.counts = [[0 for r in xrange(rows+2)] for c in xrange(cols+2)] 14 | self.revealed = [[0 for r in xrange(rows+2)] for c in xrange(cols+2)] 15 | for i in xrange(self.bombs_count): 16 | bomb_row = random.randint(1, rows) 17 | bomb_col = random.randint(1, cols) 18 | if not self.bombs[bomb_row][bomb_col]: 19 | self.bombs[bomb_row][bomb_col] = 1 20 | for row in [bomb_row-1, bomb_row, bomb_row+1]: 21 | for col in [bomb_col-1, bomb_col, bomb_col+1]: 22 | self.counts[row][col] += 1 23 | 24 | def __str__(self): 25 | line1, line2, line3 = " ", " ", " " 26 | for c in xrange(1, self.cols+1): 27 | label = str(c) 28 | while len(label) < 3: 29 | label = " " + label 30 | line1 += " " + label[0] 31 | line2 += " " + label[1] 32 | line3 += " " + label[2] 33 | parts = [line1, '\n', line2, '\n', line3, '\n'] 34 | parts.append((' +' + '-' * (2*self.cols+1)) + '+\n') 35 | for r in xrange(1, self.rows+1): 36 | label = str(r) 37 | while len(label) < 3: 38 | label = " " + label 39 | parts += label + '| ' 40 | for c in xrange(1, self.cols+1): 41 | if self.revealed[r][c]: 42 | if self.bombs[r][c]: 43 | parts.append("B ") 44 | elif self.counts[r][c]: 45 | parts.append(str(self.counts[r][c]) + " ") 46 | else: 47 | parts.append(" ") 48 | else: 49 | parts.append(". ") 50 | parts += '|\n' 51 | parts += [' +' + ('-' * (2*self.cols+1)) + '+\n'] 52 | return "".join(parts) 53 | 54 | def mark_clear(self, row, col): 55 | if row < 1 or row > self.rows or col < 1 or col > self.cols: 56 | return 57 | if self.revealed[row][col]: 58 | return 59 | self.revealed[row][col] = True 60 | if self.bombs[row][col]: 61 | self.game_over = True 62 | elif self.counts[row][col] == 0: 63 | for r in xrange(row - 1, row + 2): 64 | for c in xrange(col - 1, col + 2): 65 | self.mark_clear(r, c) 66 | 67 | 68 | def mark_bomb(self, row, col): 69 | self.revealed[row][col] = True 70 | if not self.bombs[row][col]: 71 | self.game_over = True 72 | else: 73 | self.bombs_found += 1 74 | if self.bombs_found == self.bombs_count: 75 | self.game_won = True 76 | self.game_over = True 77 | 78 | prompt = "Enter row,col to clear and bomb,row,col to label a bomb.\n> " 79 | 80 | def play_game(): 81 | rows = int(sys.argv[-2]) 82 | cols = int(sys.argv[-1]) 83 | minesweeper = Minesweeper(rows, cols) 84 | while not minesweeper.game_over: 85 | line = raw_input(str(minesweeper) + prompt) 86 | try: 87 | coords = line.split(",") 88 | if coords[0].strip().lower() == "bomb": 89 | row, col = int(coords[1]), int(coords[2]) 90 | minesweeper.mark_bomb(row, col) 91 | else: 92 | row, col = int(coords[0]), int(coords[1]) 93 | minesweeper.mark_clear(row, col) 94 | except Exception as e: 95 | print(e) 96 | print(str(minesweeper)) 97 | if minesweeper.game_won: 98 | print("You win!") 99 | else: 100 | print("You lose.") 101 | 102 | 103 | class MockRandom(object): 104 | def __init__(self): 105 | self.values = [1, 3, 1, 1] 106 | 107 | def randint(self, lower, upper): 108 | return self.values.pop(0) 109 | 110 | if __name__ == "__main__": 111 | import sys 112 | if len(sys.argv) > 2 and sys.argv[-3] == "play": 113 | import random 114 | play_game() 115 | else: 116 | import unittest 117 | print("Use the option --play {rows} {cols} to play Minesweeper.") 118 | global random 119 | random = MockRandom() 120 | 121 | class Test(unittest.TestCase): 122 | def test_minesweeper(self): 123 | m = Minesweeper(4, 4) 124 | self.assertEqual(m.bombs, [[0, 0, 0, 0, 0, 0], 125 | [0, 1, 0, 1, 0, 0], 126 | [0, 0, 0, 0, 0, 0], 127 | [0, 0, 0, 0, 0, 0], 128 | [0, 0, 0, 0, 0, 0], 129 | [0, 0, 0, 0, 0, 0]]) 130 | unittest.main() 131 | 132 | -------------------------------------------------------------------------------- /Ch7. OOD/11-file-system.py: -------------------------------------------------------------------------------- 1 | # Design an in-memory file system. 2 | 3 | class Directory(object): 4 | def __init__(self, name): 5 | self.name = name 6 | self.parent = None 7 | self.files = {} 8 | self.subdirectories = {} 9 | 10 | def addDirectory(self, directory): 11 | if directory.parent: 12 | del directory.parent.subdirectories[directory.name] 13 | directory.parent = self 14 | self.subdirectories[directory.name] = directory 15 | 16 | def addFile(self, file): 17 | self.files[file.name] = file 18 | 19 | def get(self, path): 20 | parts = path.split("/") 21 | directory = self 22 | for part in parts: 23 | if part == '..': 24 | directory = directory.parent 25 | if not directory: 26 | return None 27 | elif part in directory.subdirectories: 28 | directory = directory.subdirectories[part] 29 | elif part in directory.files: 30 | return directory.files[part] 31 | else: 32 | return None 33 | return directory 34 | 35 | class File(object): 36 | def __init__(self, name, content): 37 | self.name, self.content = name, content 38 | 39 | class NoneHash(dict): 40 | def __missing__(self, item): 41 | return None 42 | 43 | import unittest 44 | 45 | class Test(unittest.TestCase): 46 | def test_directory(self): 47 | directory0 = Directory("root") 48 | directory1 = Directory("food") 49 | directory2 = Directory("vegetables") 50 | file1 = File("arugula.png", "345235434565") 51 | directory0.addDirectory(directory1) 52 | directory1.addDirectory(directory2) 53 | directory2.addFile(file1) 54 | self.assertEqual(directory0.get("food/vegetables/rutabaga.png"), None) 55 | self.assertEqual(directory0.get("food/vegetables/arugula.png"), file1) 56 | self.assertEqual(directory0.get(".."), None) 57 | self.assertEqual(directory2.get(".."), directory1) 58 | 59 | if __name__ == "__main__": 60 | unittest.main() 61 | 62 | -------------------------------------------------------------------------------- /Ch7. OOD/12-hash-table.py: -------------------------------------------------------------------------------- 1 | # Implement a hash table. 2 | 3 | class HashTable(object): 4 | def __init__(self, array_size=64): 5 | self.array = [None for _ in xrange(array_size)] 6 | self.count = 0 7 | self.capacity = 0.75 * array_size 8 | 9 | def add(self, item, value): 10 | if self.count >= self.capacity: 11 | self.expand() 12 | self.count += 1 13 | new_node = Node(item, value) 14 | index = hash(item) % len(self.array) 15 | node = self.array[index] 16 | if node: 17 | while node.next: 18 | node = node.next 19 | node.next = new_node 20 | else: 21 | self.array[index] = new_node 22 | 23 | def lookup(self, item): 24 | node = self.array[hash(item) % len(self.array)] 25 | while node: 26 | if node.item == item: 27 | return node.value 28 | node = node.next 29 | return None 30 | 31 | def delete(self, item): 32 | index = hash(item) % len(self.array) 33 | node = self.array[index] 34 | prev = None 35 | while node: 36 | if node.item == item: 37 | self.count -= 1 38 | if prev: 39 | prev.next = node.next 40 | else: 41 | self.array[index] = node.next 42 | prev = node 43 | node = node.next 44 | 45 | def expand(self): 46 | new_array = [None for _ in xrange(2 * len(self.array))] 47 | self.count = 0 48 | self.capacity *= 2 49 | self.array, prev_array = new_array, self.array 50 | for node in prev_array: 51 | while node: 52 | self.add(node.item, node.value) 53 | node = node.next 54 | 55 | def __len__(self): 56 | return self.count 57 | 58 | class Node(object): 59 | def __init__(self, item, value, next=None): 60 | self.item, self.value, self.next = item, value, next 61 | 62 | import unittest 63 | 64 | class Test(unittest.TestCase): 65 | def test_hash_table(self): 66 | ht = HashTable(8) 67 | self.assertEqual(ht.capacity, 6) 68 | self.assertEqual(len(ht), 0) 69 | self.assertEqual(len(ht.array), 8) 70 | ht.add(15, "fifteen") 71 | ht.add(7, "seven") 72 | self.assertEqual(len(ht), 2) 73 | node_seven = ht.array[7] 74 | self.assertEqual(ht.array, [None,None,None,None,None,None,None,node_seven]) 75 | self.assertEqual(node_seven.item, 15) 76 | self.assertEqual(node_seven.value, "fifteen") 77 | self.assertEqual(node_seven.next.item, 7) 78 | self.assertEqual(node_seven.next.value, "seven") 79 | self.assertEqual(node_seven.next.next, None) 80 | self.assertEqual(ht.lookup(15), "fifteen") 81 | self.assertEqual(ht.lookup(7), "seven") 82 | self.assertEqual(ht.lookup(24), None) 83 | ht.delete(24) 84 | ht.add(23, "twenty three") 85 | ht.delete(7) 86 | self.assertEqual(ht.lookup(7), None) 87 | ht.delete(15) 88 | self.assertEqual(ht.lookup(23), "twenty three") 89 | self.assertEqual(ht.lookup(15), None) 90 | self.assertEqual(len(ht), 1) 91 | ht.add(31, "thirty one") 92 | ht.add(15, "FIFTEEN") 93 | for i in xrange(70, 90): 94 | ht.add(i, "number " + str(i)) 95 | self.assertEqual(len(ht), 23) 96 | self.assertEqual(len(ht.array), 32) 97 | self.assertEqual(ht.capacity, 24) 98 | node_fifteen = ht.array[15] 99 | self.assertEqual(node_fifteen.value, "FIFTEEN") 100 | self.assertEqual(node_fifteen.next.value, "number 79") 101 | self.assertEqual(ht.lookup(81), "number 81") 102 | 103 | if __name__ == "__main__": 104 | unittest.main() 105 | 106 | -------------------------------------------------------------------------------- /Ch8. Recursion-and-Dynamic-Programming/01-triple-step.py: -------------------------------------------------------------------------------- 1 | # Give the number of ways to climb n steps 1, 2, or 3 steps at a time. 2 | 3 | def triple_step(n): 4 | counts = [1, 1, 2] 5 | if n < 3: 6 | return counts[n] 7 | i = 2 8 | while i < n: 9 | i += 1 10 | counts[i % 3] = sum(counts) 11 | return counts[i % 3] 12 | 13 | import unittest 14 | 15 | class Test(unittest.TestCase): 16 | def test_triple_step(self): 17 | self.assertEqual(triple_step(3), 4) 18 | self.assertEqual(triple_step(7), 44) 19 | 20 | if __name__ == "__main__": 21 | unittest.main() 22 | 23 | -------------------------------------------------------------------------------- /Ch8. Recursion-and-Dynamic-Programming/02-robot-in-a-grid.py: -------------------------------------------------------------------------------- 1 | # Guide a robot with "right" and "down" steps from the upper left corner of a grid 2 | # to the lower right corner. 3 | 4 | def path_through_grid(grid): 5 | if len(grid) == 0: 6 | return [] 7 | search = [] 8 | for r, row in enumerate(grid): 9 | search.append([]) 10 | for c, blocked in enumerate(row): 11 | if r == 0 and c == 0: 12 | search[r].append("start") 13 | elif blocked: 14 | search[r].append(None) 15 | elif r > 0 and search[r-1][c]: 16 | search[r].append("down") 17 | elif c > 0 and search[r][c-1]: 18 | search[r].append("right") 19 | else: 20 | search[r].append(None) 21 | path = ["end"] 22 | r, c = len(grid) - 1, len(grid[0]) - 1 23 | if not search[r][c]: 24 | return None 25 | while c > 0 or r > 0: 26 | path.append(search[r][c]) 27 | if search[r][c] == "down": 28 | r -= 1 29 | else: 30 | c -= 1 31 | path.append("start") 32 | path.reverse() 33 | return path 34 | 35 | import unittest 36 | 37 | class Test(unittest.TestCase): 38 | def test_path_through_grid(self): 39 | grid = [[0, 0, 0, 0, 0, 0, 1], 40 | [0, 1, 1, 0, 1, 1, 0], 41 | [0, 0, 1, 0, 0, 0, 0], 42 | [1, 1, 0, 0, 0, 1, 0]] 43 | self.assertEqual(path_through_grid(grid), ["start", "right", "right", 44 | "right", "down", "down", "right", "right", "right", "down", "end"]) 45 | grid = [[0, 0, 0, 0, 0, 0, 1], 46 | [0, 1, 0, 1, 1, 1, 0], 47 | [0, 0, 0, 1, 0, 0, 0], 48 | [1, 1, 0, 0, 0, 1, 0]] 49 | self.assertEqual(path_through_grid(grid), None) 50 | 51 | if __name__ == "__main__": 52 | unittest.main() 53 | 54 | -------------------------------------------------------------------------------- /Ch8. Recursion-and-Dynamic-Programming/03-magic-index.py: -------------------------------------------------------------------------------- 1 | # Find a magic index in a sorted array. 2 | 3 | def magic_index_distinct(array): 4 | if len(array) == 0 or array[0] > 0 or array[-1] < len(array) - 1: 5 | return None 6 | return magic_index_distinct_bounds(array, 0, len(array)) 7 | 8 | def magic_index_distinct_bounds(array, lower, upper): 9 | if lower == upper: 10 | return None 11 | middle = (lower + upper) / 2 12 | if array[middle] == middle: 13 | return middle 14 | elif array[middle] > middle: 15 | return magic_index_distinct_bounds(array, lower, middle) 16 | else: 17 | return magic_index_distinct_bounds(array, middle+1, upper) 18 | 19 | def magic_index(array): 20 | index = 0 21 | while index < len(array): 22 | if index == array[index]: 23 | return index 24 | index = max(array[index], index + 1) 25 | return None 26 | 27 | import unittest 28 | 29 | class Test(unittest.TestCase): 30 | def test_magic_index_distinct(self): 31 | self.assertEqual(magic_index_distinct([3,4,5]), None) 32 | self.assertEqual(magic_index_distinct([-2,-1,0,2]), None) 33 | self.assertEqual(magic_index_distinct([-20,0,1,2,3,4,5,6,20]), None) 34 | self.assertEqual(magic_index_distinct([-20,0,1,2,3,4,5,7,20]), 7) 35 | self.assertEqual(magic_index_distinct([-20,1,2,3,4,5,6,20]), 4) 36 | 37 | def test_magic_index(self): 38 | self.assertEqual(magic_index([3,4,5]), None) 39 | self.assertEqual(magic_index([-2,-1,0,2]), None) 40 | self.assertEqual(magic_index([-20,0,1,2,3,4,5,6,20]), None) 41 | self.assertEqual(magic_index([-20,0,1,2,3,4,5,7,20]), 7) 42 | self.assertEqual(magic_index([-20,1,2,3,4,5,6,20]), 1) 43 | self.assertEqual(magic_index([-20,5,5,5,5,5,6,20]), 5) 44 | 45 | if __name__ == "__main__": 46 | unittest.main() 47 | 48 | -------------------------------------------------------------------------------- /Ch8. Recursion-and-Dynamic-Programming/04-power-set.py: -------------------------------------------------------------------------------- 1 | # Compute the power set of a set. 2 | 3 | def power_set(set0): 4 | ps = {frozenset()} 5 | for element in set0: 6 | additions = set() 7 | for subset in ps: 8 | additions.add(subset.union(element)) 9 | ps = ps.union(additions) 10 | return ps 11 | 12 | import unittest 13 | 14 | class Test(unittest.TestCase): 15 | def test_power_set(self): 16 | s = {'a', 'b', 'c', 'd'} 17 | ps = power_set(s) 18 | self.assertEqual(len(ps), 16) 19 | subsets = [set(), {'a'}, {'b'}, {'c'}, {'d'}, 20 | {'a', 'b'}, {'a', 'c'}, {'a', 'd'}, {'b', 'c'}, {'b', 'd'}, {'c', 'd'}, 21 | {'a', 'b', 'c'}, {'a', 'b', 'd'}, {'a', 'c', 'd'}, {'b', 'c', 'd'}, s] 22 | self.assertEqual(ps, set([frozenset(s) for s in subsets])) 23 | 24 | if __name__ == "__main__": 25 | unittest.main() 26 | 27 | -------------------------------------------------------------------------------- /Ch8. Recursion-and-Dynamic-Programming/05-recursive-multiply.py: -------------------------------------------------------------------------------- 1 | # Multiply two positive integers without *. 2 | 3 | # O(lg(min(a, b))) 4 | 5 | def multiply(a, b): 6 | if a > b: 7 | n, m = a, b 8 | else: 9 | n, m = b, a 10 | product = 0 11 | while m: 12 | bit = m & -m 13 | m ^= bit 14 | exponent = bit.bit_length() - 1 15 | product += n << exponent 16 | return product 17 | 18 | import unittest 19 | 20 | class Test(unittest.TestCase): 21 | def test_multiply(self): 22 | self.assertEqual(multiply(2, 2), 4) 23 | self.assertEqual(multiply(1, 125), 125) 24 | self.assertEqual(multiply(7, 11), 77) 25 | self.assertEqual(multiply(10000000010, 21), 210000000210) 26 | 27 | if __name__ == "__main__": 28 | unittest.main() 29 | 30 | -------------------------------------------------------------------------------- /Ch8. Recursion-and-Dynamic-Programming/06-towers-of-hanoi.py: -------------------------------------------------------------------------------- 1 | # Move the blocks on tower1 to tower3. 2 | 3 | def towers_of_hanoi(tower1, tower2, tower3, n=None): 4 | if n is None: 5 | n = len(tower1.discs) 6 | if n == 0: 7 | return 8 | towers_of_hanoi(tower1, tower3, tower2, n - 1) 9 | disc = tower1.discs.pop() 10 | #print("Moving disc {} from {} to {}.".format(disc, tower1, tower3)) 11 | tower3.discs.append(disc) 12 | towers_of_hanoi(tower2, tower1, tower3, n - 1) 13 | 14 | class Tower(object): 15 | def __init__(self, name, discs=None): 16 | self.name = name 17 | if discs: 18 | self.discs = discs 19 | else: 20 | self.discs = [] 21 | 22 | def __str__(self): 23 | return self.name 24 | 25 | import unittest 26 | 27 | class Test(unittest.TestCase): 28 | def test_towers_of_hanoi(self): 29 | tower1 = Tower("Tower1", ["6", "5", "4", "3", "2", "1"]) 30 | tower2 = Tower("Tower2") 31 | tower3 = Tower("Tower3") 32 | towers_of_hanoi(tower1, tower2, tower3) 33 | self.assertEqual(tower1.discs, []) 34 | self.assertEqual(tower2.discs, []) 35 | self.assertEqual(tower3.discs, ["6", "5", "4", "3", "2", "1"]) 36 | 37 | if __name__ == "__main__": 38 | unittest.main() 39 | 40 | -------------------------------------------------------------------------------- /Ch8. Recursion-and-Dynamic-Programming/07-permutations-without-dups.py: -------------------------------------------------------------------------------- 1 | # List all permutations of a string that contains no duplicate letters. 2 | 3 | def permutations(string): 4 | return partial_permutations("", string) 5 | 6 | def partial_permutations(partial, letters_left): 7 | if len(letters_left) == 0: 8 | return [partial] 9 | permutations = [] 10 | for i, letter in enumerate(letters_left): 11 | next_letters_left = letters_left[:i] + letters_left[(i+1):] 12 | permutations += partial_permutations(partial + letter, next_letters_left) 13 | return permutations 14 | 15 | import unittest 16 | 17 | class Test(unittest.TestCase): 18 | def test_permutations(self): 19 | self.assertEqual(permutations("ABCD"), ["ABCD", "ABDC", "ACBD", "ACDB", 20 | "ADBC", "ADCB", "BACD", "BADC", "BCAD", "BCDA", "BDAC", "BDCA", 21 | "CABD", "CADB", "CBAD", "CBDA", "CDAB", "CDBA", "DABC", "DACB", 22 | "DBAC", "DBCA", "DCAB", "DCBA"]) 23 | 24 | if __name__ == "__main__": 25 | unittest.main() 26 | 27 | -------------------------------------------------------------------------------- /Ch8. Recursion-and-Dynamic-Programming/08-permutations-with-dups.py: -------------------------------------------------------------------------------- 1 | # List all permutation of the letters in the given string. 2 | 3 | def permutations(string): 4 | return partial_permutations("", sorted(string)) 5 | 6 | def partial_permutations(partial, letters): 7 | if len(letters) == 0: 8 | return [partial] 9 | permutations = [] 10 | previous_letter = None 11 | for i, letter in enumerate(letters): 12 | if letter == previous_letter: 13 | continue 14 | next_partial = partial + letter 15 | next_letters = letters[:i] + letters[(i+1):] 16 | permutations += partial_permutations(next_partial, next_letters) 17 | previous_letter = letter 18 | return permutations 19 | 20 | import unittest 21 | 22 | class Test(unittest.TestCase): 23 | def test_permutations(self): 24 | self.assertEqual(permutations("abca"), ["aabc", "aacb", "abac", "abca", 25 | "acab", "acba", "baac", "baca", "bcaa", "caab", "caba", "cbaa"]) 26 | self.assertEqual(permutations("ABCD"), ["ABCD", "ABDC", "ACBD", "ACDB", 27 | "ADBC", "ADCB", "BACD", "BADC", "BCAD", "BCDA", "BDAC", "BDCA", 28 | "CABD", "CADB", "CBAD", "CBDA", "CDAB", "CDBA", "DABC", "DACB", 29 | "DBAC", "DBCA", "DCAB", "DCBA"]) 30 | 31 | if __name__ == "__main__": 32 | unittest.main() 33 | 34 | -------------------------------------------------------------------------------- /Ch8. Recursion-and-Dynamic-Programming/09-parens.py: -------------------------------------------------------------------------------- 1 | # List all valid strings containing n opening and n closing parenthesis. 2 | 3 | # Note that parens1 happens to be faster and more space efficient than parens2, 4 | # which is faster than parens3. The slowest is parens4 only because it is not 5 | # memoized. 6 | 7 | def parens1(n): 8 | parens_of_length = [[""]] 9 | if n == 0: 10 | return parens_of_length[0] 11 | for length in xrange(1, n + 1): 12 | parens_of_length.append([]) 13 | for i in xrange(length): 14 | for inside in parens_of_length[i]: 15 | for outside in parens_of_length[length - i - 1]: 16 | parens_of_length[length].append("(" + inside + ")" + outside) 17 | return parens_of_length[n] 18 | 19 | def parens2(n, open_count=0, close_count=0, memo=None): 20 | if open_count + close_count == 2 * n: 21 | return [""] 22 | key = (n - open_count - close_count, open_count) 23 | if memo is None: 24 | memo = {} 25 | elif key in memo: 26 | return memo[key] 27 | parens = [] 28 | if open_count < n: 29 | parens += ["(" + end for end in parens2(n, open_count+1, close_count, memo)] 30 | if close_count < open_count: 31 | parens += [")" + end for end in parens2(n, open_count, close_count+1, memo)] 32 | memo[key] = parens 33 | return parens 34 | 35 | def parens3(n): 36 | return parens_memo3(n, 0, 0, {}) 37 | 38 | def parens_memo3(n, open_count, close_count, memo): 39 | if open_count + close_count == 2 * n: 40 | return [""] 41 | key = (n - open_count - close_count, open_count) 42 | if key in memo: 43 | return memo[key] 44 | parens = [] 45 | if open_count < n: 46 | for end in parens_memo3(n, open_count + 1, close_count, memo): 47 | parens.append("(" + end) 48 | if close_count < open_count: 49 | for end in parens_memo3(n, open_count, close_count + 1, memo): 50 | parens.append(")" + end) 51 | memo[key] = parens 52 | return parens 53 | 54 | def parens4(n, partial="", open_count=0, close_count=0): 55 | if open_count + close_count == 2 * n: 56 | return [partial] 57 | parens = [] 58 | if open_count < n: 59 | parens += parens4(n, partial + "(", open_count + 1, close_count) 60 | if close_count < open_count: 61 | parens += parens4(n, partial + ")", open_count, close_count + 1) 62 | return parens 63 | 64 | import unittest 65 | 66 | class Test(unittest.TestCase): 67 | def test_parens1(self): 68 | self.assertEqual(parens1(1), ["()"]) 69 | self.assertEqual(parens1(2), ["()()", "(())"]) 70 | self.assertEqual(parens1(3), ["()()()", "()(())", "(())()", "(()())", 71 | "((()))"]) 72 | 73 | def test_parens2(self): 74 | self.assertEqual(parens2(1), ["()"]) 75 | self.assertEqual(parens2(2), ["(())", "()()"]) 76 | self.assertEqual(parens2(3), ["((()))", "(()())", "(())()", "()(())", 77 | "()()()"]) 78 | self.assertEqual(set(parens1(7)), set(parens2(7))) 79 | 80 | def test_parens3(self): 81 | self.assertEqual(parens3(1), ["()"]) 82 | self.assertEqual(parens3(2), ["(())", "()()"]) 83 | self.assertEqual(parens3(3), ["((()))", "(()())", "(())()", "()(())", 84 | "()()()"]) 85 | self.assertEqual(set(parens1(7)), set(parens3(7))) 86 | 87 | def test_parens4(self): 88 | self.assertEqual(parens4(1), ["()"]) 89 | self.assertEqual(parens4(2), ["(())", "()()"]) 90 | self.assertEqual(parens4(3), ["((()))", "(()())", "(())()", "()(())", 91 | "()()()"]) 92 | self.assertEqual(set(parens1(7)), set(parens4(7))) 93 | 94 | if __name__ == "__main__": 95 | unittest.main() 96 | 97 | -------------------------------------------------------------------------------- /Ch8. Recursion-and-Dynamic-Programming/10-paint-fill.py: -------------------------------------------------------------------------------- 1 | # Fill in the region containing the point with the given color. 2 | 3 | def paint_fill(image, x, y, color): 4 | if x < 0 or y < 0 or len(image) <= y or len(image[y]) <= x: 5 | return 6 | old_color = image[y][x] 7 | if old_color == color: 8 | return 9 | paint_fill_color(image, x, y, color, old_color) 10 | 11 | def paint_fill_color(image, x, y, new_color, old_color): 12 | if image[y][x] != old_color: 13 | return 14 | image[y][x] = new_color 15 | if y > 0: 16 | paint_fill_color(image, x, y - 1, new_color, old_color) 17 | if y < len(image) - 1: 18 | paint_fill_color(image, x, y + 1, new_color, old_color) 19 | if x > 0: 20 | paint_fill_color(image, x - 1, y, new_color, old_color) 21 | if x < len(image[y]) - 1: 22 | paint_fill_color(image, x + 1, y, new_color, old_color) 23 | 24 | import unittest 25 | 26 | class Test(unittest.TestCase): 27 | def test_paint_fill(self): 28 | image1 = [[10, 10, 10, 10, 10, 10, 10, 40], 29 | [30, 20, 20, 10, 10, 40, 40, 40], 30 | [10, 10, 20, 20, 10, 10, 10, 10], 31 | [10, 10, 30, 20, 20, 20, 20, 10], 32 | [40, 40, 10, 10, 10, 10, 10, 10]] 33 | image2 = [[30, 30, 30, 30, 30, 30, 30, 40], 34 | [30, 20, 20, 30, 30, 40, 40, 40], 35 | [10, 10, 20, 20, 30, 30, 30, 30], 36 | [10, 10, 30, 20, 20, 20, 20, 30], 37 | [40, 40, 30, 30, 30, 30, 30, 30]] 38 | image3 = [[30, 30, 30, 30, 30, 30, 30, 40], 39 | [30, 20, 20, 30, 30, 40, 40, 40], 40 | [30, 30, 20, 20, 30, 30, 30, 30], 41 | [30, 30, 30, 20, 20, 20, 20, 30], 42 | [40, 40, 30, 30, 30, 30, 30, 30]] 43 | paint_fill(image1, 3, 1, 30) 44 | self.assertEqual(image1, image2) 45 | paint_fill(image1, 3, 1, 10) 46 | paint_fill(image1, 3, 1, 30) 47 | self.assertEqual(image1, image3) 48 | 49 | if __name__ == "__main__": 50 | unittest.main() 51 | 52 | -------------------------------------------------------------------------------- /Ch8. Recursion-and-Dynamic-Programming/11-coins.py: -------------------------------------------------------------------------------- 1 | # Count the number of ways to make change for a given number of cents. 2 | 3 | # Why settle for a slower algorithm when a constant time algorithm exists? 4 | 5 | def coins1(cents): 6 | count = 0 7 | for c in xrange(cents, -1, -25): 8 | count += coins1_pnd(c) 9 | return count 10 | 11 | def coins1_pnd(cents): 12 | count = 0 13 | for c in xrange(cents, -1, -10): 14 | count += (c / 5) + 1 15 | return count 16 | 17 | def coins2(cents): 18 | n = cents / 5 19 | return int(n*n*n/60.0 + n*n*9/40.0 + n*53/60.0 + 301/240.0) 20 | 21 | import unittest 22 | 23 | class Test(unittest.TestCase): 24 | def test_coins1(self): 25 | self.assertEqual(coins1(0), 1) 26 | self.assertEqual(coins1(1), 1) 27 | self.assertEqual(coins1(4), 1) 28 | self.assertEqual(coins1(5), 2) 29 | self.assertEqual(coins1(15), 6) 30 | self.assertEqual(coins1(17), 6) 31 | self.assertEqual(coins1(20), 9) 32 | self.assertEqual(coins1(25), 13) 33 | self.assertEqual(coins1(52), 49) 34 | 35 | def test_coins2(self): 36 | for c in xrange(1000): 37 | self.assertEqual(coins1(c), coins2(c)) 38 | self.assertEqual(coins2(1000000), 133342333510001) 39 | 40 | if __name__ == "__main__": 41 | unittest.main() 42 | 43 | -------------------------------------------------------------------------------- /Ch8. Recursion-and-Dynamic-Programming/12-eight-queens.py: -------------------------------------------------------------------------------- 1 | # Find all arrangements of eight queens on a chess board that cannot attack 2 | # each other. 3 | 4 | STEPS = [ 5 | lambda b: (b << 1) & 0xFEFEFEFEFEFEFEFE, 6 | lambda b: (b << 7) & 0x7F7F7F7F7F7F7F7F, 7 | lambda b: (b << 9) & 0xFEFEFEFEFEFEFEFE, 8 | lambda b: (b << 8) & 0xFFFFFFFFFFFFFFFF, 9 | lambda b: (b >> 1) & 0x7F7F7F7F7F7F7F7F, 10 | lambda b: (b >> 7) & 0xFEFEFEFEFEFEFEFE, 11 | lambda b: (b >> 9) & 0x7F7F7F7F7F7F7F7F, 12 | lambda b: (b >> 8)] 13 | 14 | def eight_queens(): 15 | return eight_queens_partial(0, -1, 0xFF00000000000000) 16 | 17 | def eight_queens_partial(queens, available, row): 18 | if row == 0: 19 | return [queens] 20 | placements = [] 21 | possibility = available & row 22 | next_row = row >> 8 23 | while possibility: 24 | queen = possibility & -possibility 25 | possibility ^= queen 26 | next_queens = queens | queen 27 | next_available = available & ~queen_reach(queen) 28 | placements += eight_queens_partial(next_queens, next_available, next_row) 29 | return placements 30 | 31 | def queen_reach(bit): 32 | reach = bit 33 | for step in STEPS: 34 | move = bit 35 | while move: 36 | reach |= move 37 | move = step(move) 38 | return reach 39 | 40 | def show(placement): 41 | parts = ["\n+-----------------+\n"] 42 | for row in xrange(8): 43 | parts.append('| ') 44 | for col in xrange(8): 45 | bit = 1 << (row * 8 + col) 46 | if bit & placement: 47 | parts.append('Q ') 48 | else: 49 | parts.append(' ') 50 | parts.append('|\n') 51 | parts.append("+-----------------+\n") 52 | return "".join(parts) 53 | 54 | import unittest 55 | 56 | class Test(unittest.TestCase): 57 | def test_eight_queens(self): 58 | placements = eight_queens() 59 | self.assertEqual(len(placements), 92) 60 | self.assertEqual(show(placements[0]), """ 61 | +-----------------+ 62 | | Q | 63 | | Q | 64 | | Q | 65 | | Q | 66 | | Q | 67 | | Q | 68 | | Q | 69 | | Q | 70 | +-----------------+ 71 | """) 72 | 73 | if __name__ == "__main__": 74 | unittest.main() 75 | 76 | -------------------------------------------------------------------------------- /Ch8. Recursion-and-Dynamic-Programming/13-stacks-of-boxes.py: -------------------------------------------------------------------------------- 1 | # Stack boxes as high as possible. 2 | 3 | def stack_boxes(boxes): 4 | sorted_boxes = sorted(boxes, lambda a, b: a.height > b.height) 5 | return stack_more_boxes(sorted_boxes, None, 0) 6 | 7 | def stack_more_boxes(boxes, base, index): 8 | if index >= len(boxes): 9 | return 0 10 | without_box_height = stack_more_boxes(boxes, base, index + 1) 11 | box = boxes[index] 12 | if (not base) or box.fits_on(base): 13 | with_box_height = box.height + stack_more_boxes(boxes, box, index + 1) 14 | if with_box_height > without_box_height: 15 | return with_box_height 16 | return without_box_height 17 | 18 | class Box(object): 19 | def __init__(self, height, width, depth): 20 | self.height, self.width, self.depth = height, width, depth 21 | 22 | def fits_on(self, base): 23 | return base.height > self.height and \ 24 | base.width > self.width and \ 25 | base.depth > self.depth 26 | 27 | import unittest 28 | 29 | class Test(unittest.TestCase): 30 | def test_stack_boxes(self): 31 | boxes = [Box(100, 100, 100)] 32 | self.assertEqual(stack_boxes(boxes), 100) 33 | boxes.append(Box(25, 25, 25)) 34 | self.assertEqual(stack_boxes(boxes), 125) 35 | boxes.append(Box(20, 5, 30)) 36 | boxes.append(Box(17, 4, 28)) 37 | self.assertEqual(stack_boxes(boxes), 137) 38 | 39 | if __name__ == "__main__": 40 | unittest.main() 41 | 42 | -------------------------------------------------------------------------------- /Ch8. Recursion-and-Dynamic-Programming/14-boolean-evaluation.py: -------------------------------------------------------------------------------- 1 | # Return the number of ways that an expression can be parenthesized and 2 | # achieve a given truth value 3 | 4 | def count_eval(expr, value, memo=None): 5 | if len(expr) % 2 == 0: 6 | return Exception("Malformed expression.") 7 | if len(expr) == 1: 8 | return int((expr == "0") ^ value) 9 | if memo is None: 10 | memo = {} 11 | elif expr in memo: 12 | counts = memo[expr] 13 | return counts[int(value)] 14 | true_count = 0 15 | for opix in xrange(1, len(expr) - 1, 2): 16 | left, op, right = expr[:opix], expr[opix], expr[(opix+1):] 17 | if op == '&': 18 | true_count += count_eval(left,True, memo) * count_eval(right,True, memo) 19 | elif op == '|': 20 | true_count += count_eval(left,True, memo) * count_eval(right,True, memo) 21 | true_count += count_eval(left,False,memo) * count_eval(right,True, memo) 22 | true_count += count_eval(left,True, memo) * count_eval(right,False,memo) 23 | elif op == '^': 24 | true_count += count_eval(left,True, memo) * count_eval(right,False,memo) 25 | true_count += count_eval(left,False,memo) * count_eval(right,True, memo) 26 | else: 27 | return Exception('Unknown operation.') 28 | total_count = catalan_number((len(expr) - 1) / 2) 29 | false_count = total_count - true_count 30 | counts = (false_count, true_count) 31 | memo[expr] = counts 32 | return counts[int(value)] 33 | 34 | def catalan_number(n): 35 | number = 1 36 | for i in xrange(n + 1, 2*n + 1): 37 | number *= i 38 | for i in xrange(1, n + 2): 39 | number /= i 40 | return number 41 | 42 | import unittest 43 | 44 | class Test(unittest.TestCase): 45 | def test_count_eval(self): 46 | self.assertEqual(count_eval("1", True), 1) 47 | self.assertEqual(count_eval("0", True), 0) 48 | self.assertEqual(count_eval("0", False), 1) 49 | self.assertEqual(count_eval("1&1", True), 1) 50 | self.assertEqual(count_eval("1|0", False), 0) 51 | self.assertEqual(count_eval("1^0", True), 1) 52 | self.assertEqual(count_eval("1&0&1", True), 0) 53 | self.assertEqual(count_eval("1|1^0", True), 2) 54 | self.assertEqual(count_eval("1^0|0|1", False), 2) 55 | self.assertEqual(count_eval("1^0|0|1", True), 3) 56 | self.assertEqual(count_eval("0&0&0&1^1|0", True), 10) 57 | 58 | def test_catalan_number(self): 59 | self.assertEqual(catalan_number(0), 1) 60 | self.assertEqual(catalan_number(1), 1) 61 | self.assertEqual(catalan_number(2), 2) 62 | self.assertEqual(catalan_number(3), 5) 63 | self.assertEqual(catalan_number(4), 14) 64 | self.assertEqual(catalan_number(5), 42) 65 | 66 | if __name__ == "__main__": 67 | unittest.main() 68 | 69 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Cracking-the-coding-interview-in-Python 2 | 3 | Using python to Solve all the Cracking the Coding Interview by Gayle Lackmann McDowell. 4 | Preparing for the Great Career change of 2018. 5 | 6 | ## Different Chapters 7 | - ch-01-arrays-and-strings 8 | - ch-02-linked-lists 9 | - ch-03-stacks-and-queues 10 | - ch-04-trees-and-graphs 11 | - ch-05-bit-manipulation 12 | - ch-06-math-and-logic-puzzles 13 | - ch-07-object-oriented-design 14 | - ch-08-recursion-and-dynamic-programming 15 | - ch-09-system-design-and-scalability 16 | - ch-10-sorting-and-searching 17 | - ch-11-testing 18 | - ch-12-c-and-c-plus-plus 19 | - ch-13-java 20 | - ch-14-databases 21 | - ch-15-threads-and-locks 22 | - ch-16-moderate 23 | - ch-17-hard 24 | 25 | 26 | ## Solutions in other languages 27 | https://github.com/careercup/CtCI-6th-Edition 28 | -------------------------------------------------------------------------------- /ch-10-sorting-and-searching/01-sorted-merge.py: -------------------------------------------------------------------------------- 1 | # Merge array b into array a given that array a contains len(b) extra space at 2 | # the end. 3 | 4 | def sorted_merge(a, b): 5 | bix = len(b) - 1 6 | aix = len(a) - len(b) - 1 7 | while aix >= 0 and bix >= 0: 8 | if a[aix] > b[bix]: 9 | a[aix + bix + 1] = a[aix] 10 | aix -= 1 11 | else: 12 | a[aix + bix + 1] = b[bix] 13 | bix -= 1 14 | while bix >= 0: 15 | a[bix] = b[bix] 16 | bix -= 1 17 | 18 | import unittest 19 | 20 | class Test(unittest.TestCase): 21 | def test_sorted_merge(self): 22 | a = [33, 44, 55, 66, 88, 99, None, None, None] 23 | b = [11, 22, 77] 24 | sorted_merge(a, b) 25 | self.assertEqual(a, [11, 22, 33, 44, 55, 66, 77, 88, 99]) 26 | a = [11, 22, 55, 66, 88, None, None, None] 27 | b = [33, 44, 99] 28 | sorted_merge(a, b) 29 | self.assertEqual(a, [11, 22, 33, 44, 55, 66, 88, 99]) 30 | 31 | if __name__ == "__main__": 32 | unittest.main() 33 | 34 | -------------------------------------------------------------------------------- /ch-10-sorting-and-searching/02-group-anagrams.py: -------------------------------------------------------------------------------- 1 | # Group string anagrams together. 2 | 3 | def group_anagrams(strings): 4 | pairs = [(s, sorted(s)) for s in strings] 5 | pairs.sort(key=lambda p: p[1]) 6 | return [p[0] for p in pairs] 7 | 8 | import unittest 9 | 10 | class Test(unittest.TestCase): 11 | def test_group_anagrams(self): 12 | strings = ["cat", "bat", "rat", "arts", "tab", "tar", "car", "star"] 13 | self.assertEqual(group_anagrams(strings), 14 | ["bat", "tab", "car", "cat", "arts", "star", "rat", "tar"]) 15 | 16 | if __name__ == "__main__": 17 | unittest.main() 18 | 19 | -------------------------------------------------------------------------------- /ch-10-sorting-and-searching/03-search-in-rotated-array.py: -------------------------------------------------------------------------------- 1 | # Search for an item in a rotated array. 2 | 3 | def rotated_search(array, item, leftix=0, rightix=None): 4 | if rightix is None: 5 | rightix = len(array) 6 | if rightix <= leftix: 7 | return None 8 | middleix = (rightix + leftix) / 2 9 | left, middle = array[leftix], array[middleix] 10 | if item == middle: 11 | return middleix 12 | if item == left: 13 | return leftix 14 | if left > middle: 15 | if middle < item and item < left: 16 | return rotated_search(array, item, middleix+1, rightix) 17 | else: 18 | return rotated_search(array, item, leftix+1, middleix) 19 | elif left < item and item < middle: 20 | return rotated_search(array, item, leftix+1, middleix) 21 | else: 22 | return rotated_search(array, item, middleix+1, rightix) 23 | 24 | import unittest 25 | 26 | class Test(unittest.TestCase): 27 | def test_rotated_search(self): 28 | array = [55, 60, 65, 70, 75, 80, 85, 90, 95, 15, 20, 25, 30, 35, 40, 45] 29 | self.assertEqual(rotated_search(array, 55), 0) 30 | self.assertEqual(rotated_search(array, 60), 1) 31 | self.assertEqual(rotated_search(array, 90), 7) 32 | self.assertEqual(rotated_search(array, 95), 8) 33 | self.assertEqual(rotated_search(array, 15), 9) 34 | self.assertEqual(rotated_search(array, 30), 12) 35 | self.assertEqual(rotated_search(array, 45), 15) 36 | 37 | if __name__ == "__main__": 38 | unittest.main() 39 | 40 | -------------------------------------------------------------------------------- /ch-10-sorting-and-searching/04-sorted-search-no-size.py: -------------------------------------------------------------------------------- 1 | # Find an item in a sorted list-like object without knowing its length. 2 | 3 | def search_listy(listy, item, leftix=0, rightix=None): 4 | if rightix is None: 5 | rightix = 4 6 | right = listy[rightix] 7 | while right < item and right != -1: 8 | rightix *= 2 9 | right = listy[rightix] 10 | if right == item: 11 | return rightix 12 | if leftix == rightix: 13 | return None 14 | middleix = (leftix + rightix) / 2 15 | middle = listy[middleix] 16 | if middle == item: 17 | return middleix 18 | if middle == -1 or middle > item: 19 | return search_listy(listy, item, leftix, middleix) 20 | else: 21 | return search_listy(listy, item, middleix+1, rightix) 22 | 23 | class Listy(object): 24 | def __init__(self, array): 25 | self.array = array 26 | 27 | def __getitem__(self, ix): 28 | if ix < len(self.array): 29 | return self.array[ix] 30 | else: 31 | return -1 32 | 33 | import unittest 34 | 35 | class Test(unittest.TestCase): 36 | def test_search_listy(self): 37 | listy = Listy([-22, -11, 11, 22, 33, 44, 55, 66, 77, 88, 99]) 38 | self.assertEqual(search_listy(listy, 25), None) 39 | self.assertEqual(search_listy(listy, -22), 0) 40 | self.assertEqual(search_listy(listy, 22), 3) 41 | self.assertEqual(search_listy(listy, 66), 7) 42 | self.assertEqual(search_listy(listy, 77), 8) 43 | self.assertEqual(search_listy(listy, 99), 10) 44 | self.assertEqual(search_listy(listy, 100), None) 45 | 46 | if __name__ == "__main__": 47 | unittest.main() 48 | 49 | -------------------------------------------------------------------------------- /ch-10-sorting-and-searching/05-sparse-search.py: -------------------------------------------------------------------------------- 1 | # Search a sorted sparse array of positive integers. 2 | 3 | def sparse_search(array, item): 4 | if item < 1: 5 | return None 6 | return sparse_search_bounds(array, item, 0, len(array)) 7 | 8 | def sparse_search_bounds(array, item, left_ix, right_ix): 9 | if left_ix == right_ix: 10 | return None 11 | middle_ix = (left_ix + right_ix) / 2 12 | next_ix = middle_ix 13 | next = array[next_ix] 14 | while not next: 15 | next_ix += 1 16 | if next_ix == len(array): 17 | return sparse_search_bounds(array, item, left_ix, middle_ix) 18 | next = array[next_ix] 19 | if next == item: 20 | return next_ix 21 | if next > item: 22 | return sparse_search_bounds(array, item, left_ix, middle_ix) 23 | else: 24 | return sparse_search_bounds(array, item, next_ix+1, right_ix) 25 | 26 | import unittest 27 | 28 | class Test(unittest.TestCase): 29 | def test_sparse_search(self): 30 | array = [0, 0, 7, 0, 0, 0, 0, 0, 19, 0, 0, 0, 0, 37, 40, 0, 0, 0] 31 | self.assertEqual(sparse_search(array, 0), None) 32 | self.assertEqual(sparse_search(array, 7), 2) 33 | self.assertEqual(sparse_search(array, 19), 8) 34 | self.assertEqual(sparse_search(array, 37), 13) 35 | self.assertEqual(sparse_search(array, 40), 14) 36 | array = [0, 12, 0, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] 37 | self.assertEqual(sparse_search(array, 12), 1) 38 | self.assertEqual(sparse_search(array, 18), 3) 39 | 40 | if __name__ == "__main__": 41 | unittest.main() 42 | 43 | -------------------------------------------------------------------------------- /ch-10-sorting-and-searching/06-sort-big-file.py: -------------------------------------------------------------------------------- 1 | # Sort a big file. 2 | 3 | import os 4 | 5 | ONE_GIGABYTE = 64 6 | 7 | def sort_big_file(input_filename, output_filename): 8 | in_file = open(input_filename, "r") 9 | temp_files = [open("temp-0.txt", "w+")] 10 | index = 0 11 | next_line = in_file.readline() 12 | temp_size = 0 13 | while next_line: 14 | if temp_size + len(next_line) > ONE_GIGABYTE: 15 | temp_name = "temp-" + str(len(temp_files) + 1) + ".txt" 16 | temp_files.append(open(temp_name, "w+")) 17 | temp_size = 0 18 | temp_size += len(next_line) 19 | temp_files[-1].write(next_line) 20 | next_line = in_file.readline() 21 | min_heap = MinHeap() 22 | sorted_files = [] 23 | for f in temp_files: 24 | f.close() 25 | sort_in_memory(f.name) 26 | sorted_file = open(f.name, "r") 27 | min_heap.add((sorted_file.readline(), sorted_file)) 28 | out_file = open(output_filename, "w") 29 | next = min_heap.remove() 30 | while next: 31 | (next_line, f) = next 32 | out_file.write(next_line) 33 | line = f.readline() 34 | if line: 35 | min_heap.add((line, f)) 36 | next = min_heap.remove() 37 | for f in temp_files: 38 | f.close() 39 | os.remove(f.name) 40 | 41 | def sort_in_memory(small_filename): 42 | small_file = open(small_filename, "r") 43 | lines = small_file.readlines() 44 | small_file.close() 45 | small_file = open(small_filename, "w") 46 | lines.sort() 47 | small_file.writelines(lines) 48 | small_file.close() 49 | 50 | class MinHeap(object): 51 | def __init__(self): 52 | self.data = [None] 53 | 54 | def add(self, item): 55 | self.data.append(item) 56 | ix = len(self.data) - 1 57 | while ix and (item < self.data[ix/2]): 58 | self.data[ix], self.data[ix/2] = self.data[ix/2], item 59 | ix /= 2 60 | 61 | def remove(self): 62 | if len(self.data) == 1: 63 | return None 64 | if len(self.data) == 2: 65 | return self.data.pop() 66 | minimum = self.data[1] 67 | self.data[1] = self.data.pop() 68 | ix = 1 69 | while 2*ix < len(self.data): 70 | child_ix = 2*ix 71 | if 2*ix+1 < len(self.data) and self.data[child_ix] > self.data[2*ix+1]: 72 | child_ix = 2*ix + 1 73 | if self.data[child_ix] >= self.data[ix]: 74 | break 75 | self.data[child_ix], self.data[ix] = self.data[ix], self.data[child_ix] 76 | ix = child_ix 77 | return minimum 78 | 79 | import unittest 80 | 81 | class Test(unittest.TestCase): 82 | def test_min_heap(self): 83 | mh = MinHeap() 84 | mh.add(27) 85 | mh.add(22) 86 | mh.add(20) 87 | mh.add(31) 88 | mh.add(35) 89 | mh.add(19) 90 | mh.add(30) 91 | mh.add(18) 92 | self.assertEqual(mh.remove(), 18) 93 | self.assertEqual(mh.remove(), 19) 94 | self.assertEqual(mh.remove(), 20) 95 | self.assertEqual(mh.remove(), 22) 96 | self.assertEqual(mh.remove(), 27) 97 | self.assertEqual(mh.remove(), 30) 98 | self.assertEqual(mh.remove(), 31) 99 | self.assertEqual(mh.remove(), 35) 100 | 101 | def test_sort_big_file(self): 102 | big_file = open("big-input-file.txt", "w") 103 | big_file.write("""Haskell 104 | Golang 105 | Erlang 106 | Ruby 107 | Chess 108 | Arimaa 109 | AlphaGo 110 | Self-driving cars 111 | Convolutional neural networks 112 | TensorFlow 113 | Fourier transform 114 | Coq 115 | CFDG 116 | Processing 117 | Art festival 118 | Scrabble 119 | Bananagrams 120 | Frisbee 121 | Basketball 122 | Soccer 123 | Tennis 124 | Quantum computers 125 | Encryption 126 | Topology 127 | Homotopy 128 | Combinatorics 129 | Generating functions 130 | Matrix completion 131 | """) 132 | ordered = """AlphaGo 133 | Arimaa 134 | Art festival 135 | Bananagrams 136 | Basketball 137 | CFDG 138 | Chess 139 | Combinatorics 140 | Convolutional neural networks 141 | Coq 142 | Encryption 143 | Erlang 144 | Fourier transform 145 | Frisbee 146 | Generating functions 147 | Golang 148 | Haskell 149 | Homotopy 150 | Matrix completion 151 | Processing 152 | Quantum computers 153 | Ruby 154 | Scrabble 155 | Self-driving cars 156 | Soccer 157 | Tennis 158 | TensorFlow 159 | Topology 160 | """ 161 | big_file.close() 162 | sort_big_file("big-input-file.txt", "big-output-file.txt") 163 | output_file = open("big-output-file.txt", "r") 164 | self.assertEqual("".join(output_file.readlines()), ordered) 165 | output_file.close() 166 | os.remove("big-output-file.txt") 167 | os.remove("big-input-file.txt") 168 | 169 | if __name__ == "__main__": 170 | unittest.main() 171 | 172 | -------------------------------------------------------------------------------- /ch-10-sorting-and-searching/07-missing-int.py: -------------------------------------------------------------------------------- 1 | # Find an integer that is not contained in a list of 4 billion integers. 2 | 3 | import random 4 | 5 | def missing_int(integer_list): 6 | guess = random.randint(0, (1 << 64) - 1) 7 | for integer in integer_list: 8 | if integer == guess: 9 | return missing_int(integer_list) 10 | return guess 11 | 12 | import unittest 13 | 14 | class Test(unittest.TestCase): 15 | def test_missing_int(self): 16 | integer_list = [400, 438, 998098093, 171, 10003] 17 | integer = missing_int(integer_list) 18 | self.assertFalse( integer in integer_list) 19 | 20 | if __name__ == "__main__": 21 | unittest.main() 22 | 23 | -------------------------------------------------------------------------------- /ch-10-sorting-and-searching/08-find-duplicates.py: -------------------------------------------------------------------------------- 1 | # Find the duplicates in an array that contains numbers between 0 and 32000. 2 | 3 | def find_duplicates(array): 4 | bv = [0] * 512 5 | duplicates = [] 6 | for n in array: 7 | bit = 1 << (n % 64) 8 | if bv[n / 64] & bit: 9 | duplicates.append(n) 10 | else: 11 | bv[n / 64] |= bit 12 | return duplicates 13 | 14 | import unittest 15 | 16 | class Test(unittest.TestCase): 17 | def test_find_duplicates(self): 18 | array = [1, 2, 3, 4, 55, 20000, 20001, 20002, 20003, 17, 18, 19, 20, 22, 23, 19 | 7, 2, 3, 3, 55, 20002, 20004, 20005, 20006, 16, 18, 22, 24, 25, 26] 20 | self.assertEqual(find_duplicates(array), [2, 3, 3, 55, 20002, 18, 22]) 21 | 22 | if __name__ == "__main__": 23 | unittest.main() 24 | 25 | -------------------------------------------------------------------------------- /ch-10-sorting-and-searching/09-sorted-matrix-search.py: -------------------------------------------------------------------------------- 1 | # Search a sorted martix. 2 | 3 | def sorted_matrix_search(mat, item): 4 | if len(mat) == 0: 5 | return None 6 | return sorted_matrix_search_bounds(mat, item, 0, len(mat[0]), 0, len(mat)) 7 | 8 | def sorted_matrix_search_bounds(mat, item, x1, x2, y1, y2): 9 | if x1 == x2 or y1 == y2: 10 | return None 11 | row, col = (y1 + y2)/2, (x1 + x2)/2 12 | middle = mat[row][col] 13 | if middle == item: 14 | return (row, col) 15 | if middle > item: 16 | found = sorted_matrix_search_bounds(mat, item, x1, col, y1, row) or \ 17 | sorted_matrix_search_bounds(mat, item, col, col+1, y1, row) or \ 18 | sorted_matrix_search_bounds(mat, item, x1, col, row, row+1) 19 | if found: 20 | return found 21 | else: 22 | found = sorted_matrix_search_bounds(mat, item, col+1, x2, row+1, y2) or \ 23 | sorted_matrix_search_bounds(mat, item, col, col+1, row+1, y2) or \ 24 | sorted_matrix_search_bounds(mat, item, col+1, x2, row, row+1) 25 | if found: 26 | return found 27 | return sorted_matrix_search_bounds(mat, item, x1, col, row+1, y2) or \ 28 | sorted_matrix_search_bounds(mat, item, col+1, x2, y1, row) 29 | 30 | import unittest 31 | 32 | class Test(unittest.TestCase): 33 | def test_sorted_matrix_search(self): 34 | mat = [[1, 2, 3, 4, 5, 6, 7, 8, 9], 35 | [5, 10, 15, 20, 25, 30, 35, 40, 45], 36 | [10, 20, 30, 40, 50, 60, 70, 80, 90], 37 | [13, 23, 33, 43, 53, 63, 73, 83, 93], 38 | [14, 24, 34, 44, 54, 64, 74, 84, 94], 39 | [15, 25, 35, 45, 55, 65, 75, 85, 95], 40 | [16, 26, 36, 46, 56, 66, 77, 88, 99]] 41 | self.assertEqual(sorted_matrix_search(mat, 10), (1,1)) 42 | self.assertEqual(sorted_matrix_search(mat, 13), (3,0)) 43 | self.assertEqual(sorted_matrix_search(mat, 14), (4,0)) 44 | self.assertEqual(sorted_matrix_search(mat, 16), (6,0)) 45 | self.assertEqual(sorted_matrix_search(mat, 56), (6,4)) 46 | self.assertEqual(sorted_matrix_search(mat, 65), (5,5)) 47 | self.assertEqual(sorted_matrix_search(mat, 74), (4,6)) 48 | self.assertEqual(sorted_matrix_search(mat, 99), (6,8)) 49 | 50 | if __name__ == "__main__": 51 | unittest.main() 52 | 53 | -------------------------------------------------------------------------------- /ch-10-sorting-and-searching/10-rank-from-stream.py: -------------------------------------------------------------------------------- 1 | # Record the number of elements lower than any given elements in a stream. 2 | 3 | class RankNode(object): 4 | def __init__(self, data=None): 5 | self.data = data 6 | self.left, self.right = None, None 7 | if self.data is None: 8 | self.count = 0 9 | else: 10 | self.count = 1 11 | self.left_count = 0 12 | 13 | def track(self, item): 14 | if self.data is None: 15 | self.data = item 16 | self.count = 1 17 | elif self.data == item: 18 | self.count += 1 19 | elif self.data > item: 20 | if self.left: 21 | self.left.track(item) 22 | else: 23 | self.left = RankNode(item) 24 | self.left_count += 1 25 | else: 26 | if self.right: 27 | self.right.track(item) 28 | else: 29 | self.right = RankNode(item) 30 | 31 | def get_rank(self, item): 32 | if self.data is None: 33 | return 0 34 | elif self.data < item: 35 | if self.right: 36 | return self.count + self.left_count + self.right.get_rank(item) 37 | else: 38 | return self.count + self.left_count 39 | elif self.data > item: 40 | if self.left: 41 | return self.left.get_rank(item) 42 | else: 43 | return 0 44 | else: 45 | return self.left_count 46 | 47 | import unittest 48 | 49 | class Test(unittest.TestCase): 50 | def test_rank_tree(self): 51 | rt = RankNode() 52 | self.assertEqual(rt.get_rank(20), 0) 53 | rt.track(11) 54 | self.assertEqual(rt.get_rank(20), 1) 55 | self.assertEqual(rt.get_rank(10), 0) 56 | rt.track(30) 57 | rt.track(7) 58 | rt.track(7) 59 | rt.track(10) 60 | rt.track(15) 61 | rt.track(7) 62 | rt.track(3) 63 | rt.track(22) 64 | self.assertEqual(rt.get_rank(20), 7) 65 | self.assertEqual(rt.get_rank(7), 1) 66 | self.assertEqual(rt.get_rank(8), 4) 67 | self.assertEqual(rt.get_rank(14), 6) 68 | 69 | if __name__ == "__main__": 70 | unittest.main() 71 | 72 | -------------------------------------------------------------------------------- /ch-10-sorting-and-searching/11-peaks-and-valleys.py: -------------------------------------------------------------------------------- 1 | # Reorder an array into peaks and valleys. 2 | 3 | def peaks_and_valleys(array): 4 | if len(array) < 3: 5 | return 6 | for ix in xrange(len(array) - 1): 7 | # If this is a peak: 8 | if ix % 2: 9 | if array[ix] < array[ix + 1]: 10 | array[ix], array[ix + 1] = array[ix + 1], array[ix] 11 | # Otherwise, if this is a valley: 12 | else: 13 | if array[ix] > array[ix + 1]: 14 | array[ix], array[ix + 1] = array[ix + 1], array[ix] 15 | 16 | 17 | import unittest 18 | 19 | class Test(unittest.TestCase): 20 | def test_peaks_and_valleys(self): 21 | a = [12, 6, 3, 1, 0, 14, 13, 20, 22, 10] 22 | peaks_and_valleys(a) 23 | self.assertEqual(a, [6, 12, 1, 3, 0, 14, 13, 22, 10, 20]) 24 | b = [34, 55, 60, 65, 70, 75, 85, 10, 5, 16] 25 | peaks_and_valleys(b) 26 | self.assertEqual(b, [34, 60, 55, 70, 65, 85, 10, 75, 5, 16]) 27 | 28 | if __name__ == "__main__": 29 | unittest.main() 30 | 31 | --------------------------------------------------------------------------------