├── .gitignore ├── .project ├── .pydevproject ├── .settings └── org.eclipse.php.core.prefs ├── Algorithms ├── ConvexHullWorkshop │ ├── ConvexHullWorkshop.zip │ ├── graph.py │ ├── graph.pyc │ ├── graph.txt │ ├── graphParseAndPlot.py │ ├── interestingGraph.txt │ └── interestingGraphTwo.txt ├── HashTable │ ├── Hash.py │ └── output.txt ├── Homework3.py ├── KruskalWorkshop.zip ├── KruskalWorkshop │ ├── KruskalWorkshop.zip │ ├── Thumbs.db │ ├── graph.py │ ├── graph.pyc │ ├── graph.txt │ ├── graphOld.txt │ ├── graphParseAndPlot.py │ ├── interestingGraph.txt │ ├── interestingGraphTwo.txt │ └── resultFigure.png ├── MidtermCode │ ├── Main.py │ ├── MidtermCode.zip │ ├── New Text Document.txt │ ├── Questions1and2.txt │ ├── binary_tree.py │ ├── binary_tree.pyc │ ├── pageTwo.JPG │ ├── red_black_set_mod.py │ ├── red_black_set_mod.pyc │ └── test_binary_tree.py ├── SortHomework │ ├── bubble_insert_merge_sort.py │ ├── bubble_insert_merge_sort.pyc │ ├── heapSort.py │ ├── heapSort.pyc │ ├── quickSort.py │ └── quickSort.pyc ├── Thomas H. Cormen, Charles E. Leiserson, Ronald L. Rivest, Clifford Stein Introduction to Algorithms, Third Edition 2009.pdf ├── __init__.py ├── algorithmsSolutions.pdf ├── bst │ ├── Hash.py │ ├── Main.py │ ├── bst.zip │ ├── output.txt │ ├── red_black_dict_mod.py │ ├── red_black_set_mod.py │ └── test-red_black_tree_mod.py ├── bubble_insert_merge_sort.py ├── dijkstraWorkshop │ ├── dijkstraWorkshop.zip │ ├── graph.py │ ├── graph.txt │ └── graphParseAndPlot.py ├── geany_run_script.bat ├── huffmanWorkshop.zip ├── huffmanWorkshop │ ├── huffman.py │ ├── huffmanWorkshop.zip │ ├── huffman_tree.py │ ├── huffman_tree.pyc │ ├── infile2.txt │ ├── outfile.bin │ └── test.py └── workshop2.py └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | /bin/ 2 | .project 3 | .classpath -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | Algorithms 4 | 5 | 6 | 7 | 8 | 9 | org.python.pydev.PyDevBuilder 10 | 11 | 12 | 13 | 14 | 15 | org.python.pydev.pythonNature 16 | 17 | 18 | -------------------------------------------------------------------------------- /.pydevproject: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | /Algorithms 5 | /${PROJECT_DIR_NAME}/Algorithms 6 | 7 | python 2.7 8 | Default 9 | 10 | -------------------------------------------------------------------------------- /.settings/org.eclipse.php.core.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | include_path= 3 | -------------------------------------------------------------------------------- /Algorithms/ConvexHullWorkshop/ConvexHullWorkshop.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calvint/AlgorithmsOneProblems/945ea58295d277e484dfd4f5cc2cac033ccd2b2b/Algorithms/ConvexHullWorkshop/ConvexHullWorkshop.zip -------------------------------------------------------------------------------- /Algorithms/ConvexHullWorkshop/graph.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Created on Nov 8, 2014 3 | 4 | @author: Gary 5 | ''' 6 | from math import sqrt 7 | from math import pow 8 | 9 | class graph: 10 | def __init__(self): 11 | self.vertexNameArray = [] # vertex names in an array 12 | self.vertexIndexMap = {} # vertex names to index dictionary 13 | self.vertexPositionArray = [] # x,y pair position array 14 | self.edgeArray = [] # array of (vertex index pair, weight) 15 | 16 | def addVertex(self, name, x, y): 17 | self.vertexIndexMap[name] = self.vCount() 18 | self.vertexNameArray.append(name) 19 | self.vertexPositionArray.append((x,y)) 20 | 21 | def addEdge(self, vName1, vName2, weight): 22 | self.edgeArray.append((self.vertexIndexMap[vName1],self.vertexIndexMap[vName2],weight)) 23 | 24 | def vCount(self): return len(self.vertexNameArray) 25 | 26 | def eCount(self): return len(self.edgeArray) 27 | 28 | # Access functions for vertex information 29 | def vX( self, i): return self.vertexPositionArray[i][0] 30 | def vY( self, i): return self.vertexPositionArray[i][1] 31 | def vName(self, i): return self.vertexNameArray[i] 32 | 33 | # Access functions for edge information 34 | def eV0X( self, i): return self.vertexPositionArray[self.edgeArray[i][0]][0] 35 | def eV0Y( self, i): return self.vertexPositionArray[self.edgeArray[i][0]][1] 36 | def eV1X( self, i): return self.vertexPositionArray[self.edgeArray[i][1]][0] 37 | def eV1Y( self, i): return self.vertexPositionArray[self.edgeArray[i][1]][1] 38 | def eWght(self, i): return self.edgeArray[i][2] 39 | 40 | #uses the cross product to find wether the angle turns left or right 41 | def angleTurnsLeft(self, point1, middlePoint, point3): 42 | crossProduct = (self.vX(middlePoint) - self.vX(point1)) * (self.vY(point3) - self.vY(point1)) - (self.vX(point3) - self.vX(point1)) * (self.vY(middlePoint) - self.vY(point1)) 43 | if crossProduct >= 0.0: 44 | return True 45 | else: 46 | return False 47 | 48 | 49 | def findConvexHullEdges(self): 50 | #find lowest vertex 51 | yValMap = {} 52 | for i in range(self.vCount()): 53 | if self.vY(i) not in yValMap.keys(): 54 | yValMap[self.vY(i)] = [] 55 | yValMap[self.vY(i)].append(i) 56 | lowestVertexIndex = yValMap[sorted(yValMap.keys())[0]][0] 57 | 58 | 59 | #order other verticies acording to decreasing normalized x value 60 | xNormalizedMap = {} 61 | for i in range(self.vCount()): 62 | if i != lowestVertexIndex: 63 | #using formula: (Xi - X0)/(sqrt( (Xi - X0)^2 + (Yi - Y0)^2 ) 64 | normalizedXValue = (self.vX(i) - self.vX(lowestVertexIndex)) / sqrt( (self.vX(i) - self.vX(lowestVertexIndex))**2 + (self.vY(i) - self.vY(lowestVertexIndex))**2 ) 65 | if normalizedXValue not in xNormalizedMap.keys(): 66 | xNormalizedMap[normalizedXValue] = i 67 | else: 68 | if self.vX(i) < self.vX(xNormalizedMap[normalizedXValue]): 69 | xNormalizedMap[normalizedXValue] = i 70 | orderedVerticies = [lowestVertexIndex] 71 | for xValue in sorted(xNormalizedMap.keys(), reverse = True): 72 | orderedVerticies.append(xNormalizedMap[xValue]) 73 | orderedVerticies.append(lowestVertexIndex) 74 | 75 | #select final and original edges 76 | originallyTriedEdges = [] 77 | finalEdges = [(orderedVerticies[0], orderedVerticies[1])] 78 | i=0 79 | while i < len(orderedVerticies)-2: 80 | if self.angleTurnsLeft(orderedVerticies[i], orderedVerticies[i+1], orderedVerticies[i+2]): 81 | finalEdges.append((orderedVerticies[i+1],orderedVerticies[i+2])) 82 | i += 1 83 | else: 84 | orderedVerticies.pop(i+1) 85 | originallyTriedEdges.append(finalEdges.pop()) 86 | i -= 1 87 | return originallyTriedEdges, finalEdges 88 | 89 | 90 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /Algorithms/ConvexHullWorkshop/graph.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calvint/AlgorithmsOneProblems/945ea58295d277e484dfd4f5cc2cac033ccd2b2b/Algorithms/ConvexHullWorkshop/graph.pyc -------------------------------------------------------------------------------- /Algorithms/ConvexHullWorkshop/graph.txt: -------------------------------------------------------------------------------- 1 | a 20 30 2 | b 200 40 3 | c 30 350 4 | d 350 380 5 | e 100 300 6 | ---------- 7 | a b 3 8 | b c 4 9 | a c 5 10 | a d 4 11 | b d 2 12 | c d 1 13 | e a 2 14 | a e 5 15 | e c 3 16 | e d 3 17 | d e 4 18 | -------------------------------------------------------------------------------- /Algorithms/ConvexHullWorkshop/graphParseAndPlot.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Created on Nov 8, 2014 3 | 4 | @author: Gary 5 | ''' 6 | 7 | import fileinput 8 | import graph 9 | import matplotlib.pyplot as plt 10 | from matplotlib.path import Path 11 | import matplotlib.patches as patches 12 | import math 13 | 14 | def plotEdge( plot_axis, x0, y0, x1, y1, weight, clr): 15 | d0 = 4 # This is an offset so the edge is not drawn to the middle of vertex 16 | d2 = 20 # This is an offset to the end of the arrow tails 17 | dx = x1-x0 18 | dy = y1-y0 19 | length = math.sqrt(dx*dx+dy*dy) 20 | if length > 0: 21 | vx = dx/length 22 | vy = dy/length 23 | #plot_axis.plot([x0+vx*d0,x1-vx*d0],[y0+vy*d0,y1-vy*d0], color=clr) # Draw a line 24 | #plot_axis.text( x0+dx/2, y0+dy/2, weight) 25 | 26 | verts = [(x0+vy*d0,y0-vx*d0),(x0+vy*40,y0-vx*40),(x1-vx*80,y1-vy*80),(x1-vx*d0,y1-vy*d0),] 27 | codes = [Path.MOVETO,Path.CURVE4,Path.CURVE4,Path.CURVE4,] 28 | path = Path(verts,codes) 29 | patch = patches.PathPatch( path, facecolor = 'none', edgecolor = clr) 30 | plot_axis.add_patch( patch ) 31 | 32 | plot_axis.plot([x1-vx*d2+vy*3,x1-vx*d0],[y1-vy*d2-vx*3,y1-vy*d0], color=clr) 33 | plot_axis.plot([x1-vx*d2-vy*3,x1-vx*d0],[y1-vy*d2+vx*3,y1-vy*d0], color=clr) 34 | 35 | plot_axis.text( x0+dx/2+vy*10, y0+dy/2-vx*10, weight) 36 | 37 | # Parse graph.txt and populate mygraph structure. 38 | mygraph = graph.graph() 39 | isVertices = True 40 | for line in fileinput.input("graph.txt"): 41 | if isVertices: 42 | if "----------" in line: 43 | isVertices = False 44 | else: #read vertices in this part 45 | split = line.split() 46 | mygraph.addVertex(split[0],float(split[1]),float(split[2])) 47 | else: #read edges in this part 48 | split = line.split() 49 | mygraph.addEdge(split[0], split[1], float(split[2])) 50 | print line, isVertices 51 | 52 | fig = plt.figure() 53 | plt_ax = fig.add_subplot(111) 54 | 55 | 56 | 57 | # Display vertices 58 | minX = minY = 1e1000 59 | maxX = maxY = -1e1000 60 | for iV in range (0, mygraph.vCount()): 61 | x = mygraph.vX(iV) 62 | y = mygraph.vY(iV) 63 | plt_ax.plot(x,y,'wo', ms = 15) 64 | minX = min(minX,x) 65 | minY = min(minY,y) 66 | maxX = max(maxX,x) 67 | maxY = max(maxY,y) 68 | plt_ax.text(x, y, mygraph.vName(iV), ha = 'center', va = 'center') 69 | padX = .10*(maxX-minX)+10 70 | padY = .10*(maxY-minY)+10 71 | plt_ax.axis([minX-padX, maxX+padX, minY-padY, maxY+padY]) 72 | 73 | originallyTriedEdges, finalEdges = mygraph.findConvexHullEdges(); 74 | 75 | # Display edges originally tried edges 76 | for edge in originallyTriedEdges: 77 | x0 = mygraph.vX(edge[0]) 78 | y0 = mygraph.vY(edge[0]) 79 | x1 = mygraph.vX(edge[1]) 80 | y1 = mygraph.vY(edge[1]) 81 | plotEdge(plt_ax, x0, y0, x1, y1, "", 'red') 82 | 83 | # Display final edges 84 | for edge in finalEdges: 85 | x0 = mygraph.vX(edge[0]) 86 | y0 = mygraph.vY(edge[0]) 87 | x1 = mygraph.vX(edge[1]) 88 | y1 = mygraph.vY(edge[1]) 89 | plotEdge(plt_ax, x0, y0, x1, y1, "", 'black') 90 | 91 | plt.show() 92 | -------------------------------------------------------------------------------- /Algorithms/ConvexHullWorkshop/interestingGraph.txt: -------------------------------------------------------------------------------- 1 | a 20 30 2 | b 100 40 3 | c 30 250 4 | d 60 130 5 | e 150 90 6 | f 240 60 7 | g 120 200 8 | h 200 160 9 | i 260 100 10 | j 360 400 11 | l 180 350 12 | m 280 300 13 | n 80 350 14 | o 350 40 15 | ---------- 16 | a b 3 17 | a c 2 18 | a d 5 19 | b c 4 20 | b d 1 21 | b g 2 22 | c d 7 23 | c g 5 24 | e f 4 25 | e g 3 26 | e h 2 27 | f h 3 28 | f i 4 29 | g l 1 30 | g n 1 31 | h i 1 32 | h m 3 33 | i m 2 34 | i o 2 35 | j l 7 36 | j m 3 37 | j o 4 38 | l m 5 39 | l n 6 -------------------------------------------------------------------------------- /Algorithms/ConvexHullWorkshop/interestingGraphTwo.txt: -------------------------------------------------------------------------------- 1 | a 10 100 2 | b 100 200 3 | c 200 200 4 | d 300 200 5 | e 390 100 6 | f 300 10 7 | g 200 10 8 | h 100 10 9 | i 150 100 10 | ---------- 11 | a b 4 12 | b c 8 13 | b h 11 14 | a h 8 15 | h i 7 16 | h g 1 17 | i c 2 18 | i g 6 19 | c f 4 20 | g f 2 21 | c d 7 22 | d e 9 23 | d f 14 24 | e f 10 -------------------------------------------------------------------------------- /Algorithms/HashTable/Hash.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Created on Sep 22, 2014 3 | 4 | @author: Calvin 5 | ''' 6 | import random 7 | from _ast import Num 8 | 9 | 10 | 11 | #class implementing linked list 12 | class Stack(object): 13 | 14 | itemArray = None 15 | 16 | def __init__(self): 17 | self.itemArray = [] 18 | 19 | def push(self, int): 20 | self.itemArray.append(int) 21 | 22 | def pop(self): 23 | return self.pop() 24 | 25 | def search(self, int): 26 | count = 0 27 | searched = [] 28 | while self.itemArray != []: 29 | num = self.itemArray.pop() 30 | searched.append(num) 31 | count += 1 32 | if num == int: 33 | for i in range(len(searched)): 34 | self.itemArray.append(searched.pop()) 35 | return (True, count) 36 | for i in range(len(searched)): 37 | self.itemArray.append(searched.pop()) 38 | return (False, count) 39 | 40 | def delete(self, int): 41 | count = 0 42 | searched = [] 43 | while self.itemArray != []: 44 | num = self.itemArray.pop() 45 | searched.append(num) 46 | count += 1 47 | if num == int: 48 | searched.pop() 49 | for i in range(len(searched)): 50 | self.itemArray.append(searched.pop()) 51 | return (True, count) 52 | for i in range(len(searched)): 53 | self.itemArray.append(searched.pop()) 54 | return (False, count) 55 | 56 | 57 | #takes a dictionary as the hash table and a list of numbers that 58 | # must be added to the hash 59 | class HashTable(): 60 | table = None 61 | divCount = None 62 | 63 | def __init__(self, divisions): 64 | self.table = {} 65 | self.divCount = divisions 66 | 67 | def insert(self, keys): 68 | for num in keys: 69 | mod = num%100000 70 | if mod not in self.table.keys(): 71 | self.table[mod] = Stack() 72 | self.table[mod].push(num) 73 | elif mod in self.table.keys(): 74 | self.table[mod].push(num) 75 | 76 | def search(self, keys): 77 | results = [] 78 | count = 0 79 | for num in keys: 80 | mod = num%100000 81 | if mod in self.table.keys(): 82 | resultTuple = self.table[mod].search(num) 83 | count += resultTuple[1] 84 | if resultTuple[0]: 85 | results.append(num) 86 | 87 | return results, count 88 | 89 | def delete(self, keys): 90 | results = [] 91 | count = 0 92 | for num in keys: 93 | mod = num%100000 94 | if mod in self.table.keys(): 95 | resultTuple = self.table[mod].delete(num) 96 | count += resultTuple[1] 97 | if resultTuple[0]: 98 | results.append(num) 99 | return results, count 100 | 101 | 102 | def generateKeys(number, domain): 103 | keys = [] 104 | for i in range(number): 105 | keys.append(random.randrange(0,domain)) 106 | return keys 107 | 108 | def generateSmallKeys(): 109 | keys = [] 110 | for i in range(0, 10): 111 | keys.append(random.randrange(0,20)) 112 | return keys 113 | 114 | if __name__ == "__main__": 115 | hashtb = HashTable(100000) 116 | keys_one = generateKeys(10000, 2000000000) 117 | hashtb.insert(keys_one) 118 | keys_two = generateKeys(10000, 2000000000) 119 | results = hashtb.search(keys_two) 120 | print "search found this many matches: {0} \n".format(len(results[0])) 121 | print "list of common keys from search: " + str(list( set(keys_one).intersection( set(keys_two) ) ) ) 122 | print "number of comparisons: " + str(results[1]) 123 | 124 | keys_three = generateKeys(10000, 2000000000) 125 | results = hashtb.delete(keys_three) 126 | 127 | print "delete found this many matches: {0} \n".format(len(results[0])) 128 | print "list of common keys from delete: " + str(list( set(keys_one).intersection( set(keys_three) ) ) ) 129 | print "number of comparisons: " + str(results[1]) 130 | 131 | 132 | 133 | 134 | -------------------------------------------------------------------------------- /Algorithms/HashTable/output.txt: -------------------------------------------------------------------------------- 1 | search found this many matches: 0 2 | 3 | list of comon keys from search: [] 4 | delete found this many matches: 10000 5 | 6 | list of comon keys from delete: [] 7 | -------------------------------------------------------------------------------- /Algorithms/Homework3.py: -------------------------------------------------------------------------------- 1 | import time 2 | import random 3 | 4 | def findMaxCrossingSubarray(array, low, mid, high): 5 | tempSum = 0 6 | leftSum = None 7 | for i in range(mid, low - 1, -1): 8 | tempSum += array[i] 9 | if leftSum == None or tempSum > leftSum: 10 | leftSum = tempSum 11 | maxLeft = i 12 | tempSum = 0 13 | rightSum = None 14 | for i in range(mid+1, high+1): 15 | tempSum += array[i] 16 | if rightSum == None or tempSum > rightSum: 17 | rightSum = tempSum 18 | maxRight = i 19 | return (maxLeft, maxRight, leftSum+rightSum) 20 | 21 | 22 | def findMaxSubArray(array, low, high): 23 | if high == low: 24 | return (low, high, array[low]) 25 | else: 26 | mid = (low + high)/2 27 | (leftLow, leftHigh, leftSum) = findMaxSubArray(array, low, mid) 28 | (rightLow, rightHigh, rightSum) = findMaxSubArray(array, mid + 1, high) 29 | (crossingLow, crossingHigh, crossingSum) = findMaxCrossingSubarray(array, low, mid, high) 30 | if leftSum > rightSum and leftSum > crossingSum: 31 | return (leftLow, leftHigh, leftSum) 32 | if rightSum > leftSum and rightSum > crossingSum: 33 | return (rightLow, rightHigh, rightSum) 34 | else: 35 | return (crossingLow, crossingHigh, crossingSum) 36 | 37 | #array = [13, -3, -25, 20, -3, -16, -23, 18, 20, -7, 12, -5, -22, 15, -4, 7] 38 | #(Low, High, Sum) = findMaxSubArray(array, 0, len(array)-1) 39 | #print("The maximum subarray has a sum of " + str(Sum) + ", a lower index value of " + str(Low) + ", and a higher index value of " + str(High)) 40 | 41 | 42 | def bruteForceMaxSubArray(array): 43 | maxSum = None 44 | for low in range( 0, len(array)): 45 | for high in range( low, len(array)): 46 | tempSum = 0 47 | for k in range( low, high+1): 48 | tempSum += array[k] 49 | if maxSum == None or tempSum > maxSum: 50 | maxSum = tempSum 51 | maxLow = low 52 | maxHigh = high 53 | return (maxLow, maxHigh, maxSum) 54 | 55 | 56 | #array = [13, -3, -25, 20, -3, -16, -23, 18, 20, -7, 12, -5, -22, 15, -4, 7] 57 | #(Low, High, Sum) = bruteForceMaxSubArray(array) 58 | #print("The maximum subarray has a sum of " + str(Sum) + ", a lower index value of " + str(Low) + ", and a higher index value of " + str(High)) 59 | 60 | def findNForCrossover(): 61 | for n in range(1, 1000): 62 | testArray = range(n) 63 | random.shuffle(testArray) 64 | recursiveStart = time.time() 65 | findMaxSubArray(testArray, 0, len(testArray)-1) 66 | recursiveTime = time.time()-recursiveStart 67 | 68 | testArray = range(n) 69 | random.shuffle(testArray) 70 | bruteStart = time.time() 71 | bruteForceMaxSubArray(testArray) 72 | bruteTime = time.time()-bruteStart 73 | 74 | if recursiveTime < bruteTime: 75 | print("The crossover occured at n = %s" %n) 76 | return (n, recursiveTime, bruteTime) 77 | 78 | findNForCrossover() 79 | 80 | def findMaxSubArrayWithBruteBase(array, low, high): 81 | if high == low: 82 | return bruteForceMaxSubArray(array) 83 | else: 84 | mid = (low + high)/2 85 | (leftLow, leftHigh, leftSum) = findMaxSubArray(array, low, mid) 86 | (rightLow, rightHigh, rightSum) = findMaxSubArray(array, mid + 1, high) 87 | (crossingLow, crossingHigh, crossingSum) = findMaxCrossingSubarray(array, low, mid, high) 88 | if leftSum > rightSum and leftSum > crossingSum: 89 | return (leftLow, leftHigh, leftSum) 90 | if rightSum > leftSum and rightSum > crossingSum: 91 | return (rightLow, rightHigh, rightSum) 92 | else: 93 | return (crossingLow, crossingHigh, crossingSum) 94 | 95 | #array = [13, -3, -25, 20, -3, -16, -23, 18, 20, -7, 12, -5, -22, 15, -4, 7] 96 | #(Low, High, Sum) = findMaxSubArrayWithBruteBase(array, 0, len(array)-1) 97 | #print("The maximum subarray has a sum of " + str(Sum) + ", a lower index value of " + str(Low) + ", and a higher index value of " + str(High)) 98 | 99 | 100 | def findNForCrossoverWithBruteBase(): 101 | for n in range(1, 1000): 102 | testArray = range(n) 103 | random.shuffle(testArray) 104 | recursiveStart = time.time() 105 | findMaxSubArrayWithBruteBase(testArray, 0, len(testArray)-1) 106 | recursiveTime = time.time()-recursiveStart 107 | 108 | testArray = range(n) 109 | random.shuffle(testArray) 110 | bruteStart = time.time() 111 | bruteForceMaxSubArray(testArray) 112 | bruteTime = time.time()-bruteStart 113 | 114 | if recursiveTime < bruteTime: 115 | print("The crossover occured at n = %s" %n) 116 | return (n, recursiveTime, bruteTime) 117 | 118 | findNForCrossoverWithBruteBase() 119 | 120 | 121 | -------------------------------------------------------------------------------- /Algorithms/KruskalWorkshop.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calvint/AlgorithmsOneProblems/945ea58295d277e484dfd4f5cc2cac033ccd2b2b/Algorithms/KruskalWorkshop.zip -------------------------------------------------------------------------------- /Algorithms/KruskalWorkshop/KruskalWorkshop.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calvint/AlgorithmsOneProblems/945ea58295d277e484dfd4f5cc2cac033ccd2b2b/Algorithms/KruskalWorkshop/KruskalWorkshop.zip -------------------------------------------------------------------------------- /Algorithms/KruskalWorkshop/Thumbs.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calvint/AlgorithmsOneProblems/945ea58295d277e484dfd4f5cc2cac033ccd2b2b/Algorithms/KruskalWorkshop/Thumbs.db -------------------------------------------------------------------------------- /Algorithms/KruskalWorkshop/graph.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Created on Nov 8, 2014 3 | 4 | @author: Gary 5 | ''' 6 | import copy 7 | 8 | class graph: 9 | def __init__(self): 10 | self.vertexNameArray = [] # vertex names in an array 11 | self.vertexIndexMap = {} # vertex names to index dictionary 12 | self.vertexPositionArray = [] # x,y pair position array 13 | self.edgeArray = [] # array of (vertex index pair, weight) 14 | 15 | def addVertex(self, name, x, y): 16 | self.vertexIndexMap[name] = self.vCount() 17 | self.vertexNameArray.append(name) 18 | self.vertexPositionArray.append((x,y)) 19 | 20 | def addEdge(self, vName1, vName2, weight): 21 | self.edgeArray.append((self.vertexIndexMap[vName1],self.vertexIndexMap[vName2],weight)) 22 | 23 | def vCount(self): return len(self.vertexNameArray) 24 | 25 | def eCount(self): return len(self.edgeArray) 26 | 27 | # Access functions for vertex information 28 | def vX( self, i): return self.vertexPositionArray[i][0] 29 | def vY( self, i): return self.vertexPositionArray[i][1] 30 | def vName(self, i): return self.vertexNameArray[i] 31 | 32 | # Access functions for edge information 33 | def eV0X( self, i): return self.vertexPositionArray[self.edgeArray[i][0]][0] 34 | def eV0Y( self, i): return self.vertexPositionArray[self.edgeArray[i][0]][1] 35 | def eV1X( self, i): return self.vertexPositionArray[self.edgeArray[i][1]][0] 36 | def eV1Y( self, i): return self.vertexPositionArray[self.edgeArray[i][1]][1] 37 | def eWght(self, i): return self.edgeArray[i][2] 38 | 39 | def getMinSpanningTreeEdgesIndexes(self): 40 | #make a modifyable copy of vertexIndexMap 41 | vertexMapCopy = copy.deepcopy(self.vertexIndexMap) 42 | #make a dictionary of weights to edge indices 43 | weightMap = {} 44 | i=0 45 | for edge in self.edgeArray: 46 | if edge[2] not in weightMap.keys(): 47 | weightMap[edge[2]] = [i] 48 | else: 49 | weightMap[edge[2]].append(i) 50 | i+=1 51 | #make an ordered array of weights 52 | weightsArray = sorted(weightMap.keys()) 53 | results = [] 54 | for weight in weightsArray: 55 | edgeIndicies = weightMap.get(weight) 56 | for index in edgeIndicies: 57 | newGroupNum = vertexMapCopy[self.vName(self.edgeArray[index][0])] 58 | oldGroupNum = vertexMapCopy[self.vName(self.edgeArray[index][1])] 59 | #if they aren't already in the same connected group... 60 | if newGroupNum != oldGroupNum: 61 | results.append(index) 62 | #make them in the same group 63 | for name, otherGroupNumber in vertexMapCopy.items(): 64 | if otherGroupNumber == oldGroupNum: 65 | vertexMapCopy[name] = newGroupNum 66 | return results 67 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /Algorithms/KruskalWorkshop/graph.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calvint/AlgorithmsOneProblems/945ea58295d277e484dfd4f5cc2cac033ccd2b2b/Algorithms/KruskalWorkshop/graph.pyc -------------------------------------------------------------------------------- /Algorithms/KruskalWorkshop/graph.txt: -------------------------------------------------------------------------------- 1 | a 20 30 2 | b 200 40 3 | c 30 350 4 | d 350 380 5 | e 100 300 6 | ---------- 7 | a b 3 8 | b c 4 9 | a c 5 10 | a d 4 11 | b d 2 12 | c d 1 13 | e a 2 14 | e c 3 15 | e d 3 -------------------------------------------------------------------------------- /Algorithms/KruskalWorkshop/graphOld.txt: -------------------------------------------------------------------------------- 1 | a 20 30 2 | b 200 40 3 | c 30 350 4 | ---------- 5 | a b 3 6 | b c 4 7 | a c 5 8 | -------------------------------------------------------------------------------- /Algorithms/KruskalWorkshop/graphParseAndPlot.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Created on Nov 8, 2014 3 | 4 | @author: Gary 5 | ''' 6 | 7 | import fileinput 8 | import graph 9 | import matplotlib.pyplot as plt 10 | 11 | # Parse graph.txt and populate mygraph structure. 12 | mygraph = graph.graph() 13 | isVertices = True 14 | for line in fileinput.input("interestingGraphTwo.txt"): 15 | if isVertices: 16 | if "----------" in line: 17 | isVertices = False 18 | else: #read vertices in this part 19 | split = line.split() 20 | mygraph.addVertex(split[0],float(split[1]),float(split[2])) 21 | else: #read edges in this part 22 | split = line.split() 23 | mygraph.addEdge(split[0], split[1], float(split[2])) 24 | print line, isVertices 25 | 26 | # Display vertices 27 | minX = minY = 1e1000 28 | maxX = maxY = -1e1000 29 | for iV in range (0, mygraph.vCount()): 30 | x = mygraph.vX(iV) 31 | y = mygraph.vY(iV) 32 | plt.plot(x,y,'wo', ms = 15) 33 | minX = min(minX,x) 34 | minY = min(minY,y) 35 | maxX = max(maxX,x) 36 | maxY = max(maxY,y) 37 | plt.text(x, y, mygraph.vName(iV), ha = 'center', va = 'center') 38 | padX = .02*(maxX-minX)+5 39 | padY = .02*(maxY-minY)+5 40 | plt.axis([minX-padX, maxX+padX, minY-padY, maxY+padY]) 41 | 42 | # Display edges 43 | for iE in range (0, mygraph.eCount()): 44 | x0 = mygraph.eV0X(iE) 45 | y0 = mygraph.eV0Y(iE) 46 | x1 = mygraph.eV1X(iE) 47 | y1 = mygraph.eV1Y(iE) 48 | xM = (x0+x1)/2 49 | yM = (y0+y1)/2 50 | plt.plot([x0,x1],[y0,y1],color='0.9') 51 | plt.text(xM, yM, mygraph.eWght(iE)) 52 | 53 | # Find out how to find edges and do that... 54 | 55 | # add all of the edges 56 | for iE in mygraph.getMinSpanningTreeEdgesIndexes(): 57 | x0 = mygraph.eV0X(iE) 58 | y0 = mygraph.eV0Y(iE) 59 | x1 = mygraph.eV1X(iE) 60 | y1 = mygraph.eV1Y(iE) 61 | xM = (x0+x1)/2 62 | yM = (y0+y1)/2 63 | plt.plot([x0,x1],[y0,y1],color='c') 64 | plt.text(xM, yM, mygraph.eWght(iE)) 65 | 66 | plt.show() 67 | -------------------------------------------------------------------------------- /Algorithms/KruskalWorkshop/interestingGraph.txt: -------------------------------------------------------------------------------- 1 | a 20 30 2 | b 100 40 3 | c 30 250 4 | d 60 130 5 | e 150 90 6 | f 240 60 7 | g 120 200 8 | h 200 160 9 | i 260 100 10 | j 360 400 11 | l 180 350 12 | m 280 300 13 | n 80 350 14 | o 350 40 15 | ---------- 16 | a b 3 17 | a c 2 18 | a d 5 19 | b c 4 20 | b d 1 21 | b g 2 22 | c d 7 23 | c g 5 24 | e f 4 25 | e g 3 26 | e h 2 27 | f h 3 28 | f i 4 29 | g l 1 30 | g n 1 31 | h i 1 32 | h m 3 33 | i m 2 34 | i o 2 35 | j l 7 36 | j m 3 37 | j o 4 38 | l m 5 39 | l n 6 -------------------------------------------------------------------------------- /Algorithms/KruskalWorkshop/interestingGraphTwo.txt: -------------------------------------------------------------------------------- 1 | a 10 100 2 | b 100 200 3 | c 200 200 4 | d 300 200 5 | e 390 100 6 | f 300 10 7 | g 200 10 8 | h 100 10 9 | i 150 100 10 | ---------- 11 | a b 4 12 | b c 8 13 | b h 11 14 | a h 8 15 | h i 7 16 | h g 1 17 | i c 2 18 | i g 6 19 | c f 4 20 | g f 2 21 | c d 7 22 | d e 9 23 | d f 14 24 | e f 10 -------------------------------------------------------------------------------- /Algorithms/KruskalWorkshop/resultFigure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calvint/AlgorithmsOneProblems/945ea58295d277e484dfd4f5cc2cac033ccd2b2b/Algorithms/KruskalWorkshop/resultFigure.png -------------------------------------------------------------------------------- /Algorithms/MidtermCode/Main.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Created on Oct 2, 2014 3 | 4 | @author: Calvin 5 | ''' 6 | import red_black_set_mod 7 | import random 8 | import binary_tree 9 | 10 | random.seed(1) 11 | 12 | def generateKeys(number, domain): 13 | keys = [] 14 | for i in range(number): 15 | keys.append(random.randrange(0,domain)) 16 | return keys 17 | 18 | 19 | keys = generateKeys(10000, 2000000000) 20 | 21 | #red black BST 22 | rb_tree = red_black_set_mod.RedBlackTree() 23 | 24 | #new tree type 25 | diffTree = binary_tree.Node(keys[0]) 26 | 27 | 28 | #i) 10,000 inserts 29 | 30 | 31 | totalCount = 0 32 | for key in keys: 33 | node, count = rb_tree.add(key) 34 | totalCount += count 35 | print "inserted 10,000 elements into rb_tree with {0} comparisons\n\n".format(str(totalCount)) 36 | 37 | count = 0 38 | rotates = 0 39 | for key in keys[1:]: 40 | countInc, rotatesInc, root = diffTree.checkThenInsert(key, diffTree) 41 | diffTree = root 42 | count += countInc 43 | rotates += rotatesInc 44 | print "inserted 10,000 elements into new type bst with {0} comparisons and {1} rotates\n\n".format(str(count) , str(rotates)) 45 | 46 | 47 | #(ii) deleting first 1000 elements 48 | 49 | 50 | totalCount = 0 51 | for key in keys[9000:9999]: 52 | count = rb_tree.discard(key) 53 | totalCount += count 54 | print "deleted last 1000 elements added to rb_tree with {0} comparisons\n\n".format(totalCount) 55 | 56 | count = 0 57 | rotates = 0 58 | for key in keys[9000:9999]: 59 | countInc, rotatesInc, root = diffTree.delete(key, diffTree) 60 | diffTree = root 61 | count += countInc 62 | rotates += rotatesInc 63 | print "deleted last 1000 elements from new type bst with {0} comparisons and {1} rotates\n\n".format(str(count) , str(rotates)) 64 | 65 | #(iii) inserting 1000 elements 66 | 67 | 68 | rb_tree = red_black_set_mod.RedBlackTree() 69 | totalCount = 0 70 | for key in keys[:999]: 71 | node, count = rb_tree.add(key) 72 | totalCount += count 73 | print "inserted 1000 elements into rb_tree with {0} comparisons and {1} rotates\n\n".format(str(count) , str(rotates)) 74 | 75 | 76 | count = 0 77 | rotates = 0 78 | for key in keys[1:999]: 79 | countInc, rotatesInc, root = diffTree.insert(key, diffTree) 80 | diffTree = root 81 | count += countInc 82 | rotates += rotatesInc 83 | print "inserted 1000 elements into new type bst with {0} comparisons and {1} rotates\n\n".format(str(count) , str(rotates)) 84 | 85 | 86 | # (iv) deleting last 100 elements added 87 | 88 | totalCount = 0 89 | for key in keys[900:999]: 90 | count = rb_tree.discard(key) 91 | totalCount += count 92 | print "deleted last 100 elements added to rb_tree with {0} comparisons\n\n".format(totalCount) 93 | 94 | count = 0 95 | rotates = 0 96 | for key in keys[900:999]: 97 | countInc, rotatesInc, root = diffTree.delete(key, diffTree) 98 | diffTree = root 99 | count += countInc 100 | rotates += rotatesInc 101 | print "deleted last 100 elements from new type bst with {0} comparisons and {1} rotates\n\n".format(str(count) , str(rotates)) 102 | -------------------------------------------------------------------------------- /Algorithms/MidtermCode/MidtermCode.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calvint/AlgorithmsOneProblems/945ea58295d277e484dfd4f5cc2cac033ccd2b2b/Algorithms/MidtermCode/MidtermCode.zip -------------------------------------------------------------------------------- /Algorithms/MidtermCode/New Text Document.txt: -------------------------------------------------------------------------------- 1 | inserted 10,000 elements into rb_tree with 121370 comparisons 2 | 3 | 4 | inserted 10,000 elements into new type bst with 370054 comparisons and 2886 rotates 5 | 6 | 7 | deleted last 1000 elements added to rb_tree with 14820 comparisons 8 | 9 | 10 | deleted last 1000 elements from new type bst with 22079 comparisons and 107 rotates 11 | 12 | 13 | inserted 1000 elements into rb_tree with 10 comparisons and 107 rotates 14 | 15 | 16 | inserted 1000 elements into new type bst with 14287 comparisons and 0 rotates 17 | 18 | 19 | deleted last 100 elements added to rb_tree with 1109 comparisons 20 | 21 | 22 | deleted last 100 elements from new type bst with 1676 comparisons and 18 rotates 23 | -------------------------------------------------------------------------------- /Algorithms/MidtermCode/Questions1and2.txt: -------------------------------------------------------------------------------- 1 | Midterm Coding Assignment: 2 | 3 | 1) 4 | During the left rotation: 5 | a is decreased by c, 6 | c is increased by a-c, 7 | b, d, e, f, and g are left alone. 8 | 9 | During the right rotation: 10 | a is decreased by b, 11 | b is increased by a-b, 12 | c, d, e, f, g are left alone 13 | 14 | 2) 15 | i) 16 | Inserting into a normal binary search tree is O(h) where h is the height of the tree. 17 | This new System adds a few new processes to the insertion which include: 18 | incrementing parent nodes 19 | maintaining rotation rules 20 | incrementing the parent nodes takes O(h) since there are h-1 parents to increment and the 21 | incrementing process is O(1). 22 | Maintaining rotation rules is O(h) since there are only h-1 places where the rotate rules could 23 | have been violated and fixing the rotate rules takes O(1) time. 24 | 25 | -------------------------------------------------------------------------------- /Algorithms/MidtermCode/binary_tree.py: -------------------------------------------------------------------------------- 1 | class Node: 2 | """ 3 | Tree node: left and right child + data which can be any object 4 | """ 5 | def __init__(self, data, parent = None): 6 | """ 7 | Node constructor 8 | 9 | @param data node data object 10 | """ 11 | self.parent = parent 12 | self.left = None 13 | self.right = None 14 | self.data = data 15 | self.count = 1 16 | 17 | def checkThenInsert(self, data, root, compares = 0): 18 | node, parent, compares = self.lookup(data) 19 | if node != None: 20 | return compares, 0, root 21 | else: 22 | return self.insert(data, root, compares) 23 | 24 | def insert(self, data, root, compares = 0): 25 | """ 26 | Insert new node with data 27 | 28 | @param data node data object to insert 29 | """ 30 | 31 | self.count += 1 32 | if data < self.data: 33 | if self.left is None: 34 | self.left = Node(data, self) 35 | rotates, root = self.left.checkForRotates(root) 36 | return compares +1, rotates, root 37 | else: 38 | return self.left.insert(data, root, compares + 1) 39 | elif data > self.data: 40 | if self.right is None: 41 | self.right = Node(data, self) 42 | rotates, root = self.right.checkForRotates(root) 43 | return compares + 1, rotates, root 44 | else: 45 | return self.right.insert(data, root, compares + 2) 46 | else: 47 | return compares +1, 0, root 48 | 49 | def lookup(self, data, parent=None, comparesNum = 0): 50 | """ 51 | Lookup node containing data 52 | 53 | @param data node data object to look up 54 | @param parent node's parent 55 | @returns node and node's parent if found or None, None 56 | """ 57 | if data < self.data: 58 | if self.left is None: 59 | return None, None, comparesNum +1 60 | return self.left.lookup(data, self, comparesNum +1) 61 | elif data > self.data: 62 | if self.right is None: 63 | return None, None, comparesNum +2 64 | return self.right.lookup(data, self, comparesNum +2) 65 | else: 66 | return self, parent, comparesNum +2 67 | 68 | def subtractOneFromParents(self): 69 | self.count -= 1 70 | if self.parent != None: 71 | self.parent.subtractOneFromParents() 72 | 73 | def checkForRotates(self, root): 74 | rotates = 0 75 | if self.parent != None and str(self.parent) != "None": 76 | if self.parent.right == self: 77 | if self.parent.left != None and self.parent.left.left != None: 78 | if self.parent.left.left.count > self.count: 79 | root = self.parent.left.rotate_right(root) 80 | rotates +=1 81 | if self.parent.parent != None and self.parent == self.parent.parent.right and self.parent.parent.left != None: 82 | if self.parent.parent.left.count < self.count: 83 | root = self.parent.rotate_left(root) 84 | rotates +=1 85 | if self.parent.left == self: 86 | if self.parent.right != None and self.parent.right.right != None: 87 | if self.parent.right.right.count > self.count: 88 | root = self.parent.right.rotate_left(root) 89 | rotates +=1 90 | # print "***" + str(self) + "***" + str(self.parent) + "***\n" 91 | if self.parent.parent != None and self.parent == self.parent.parent.left and self.parent.parent.right != None: 92 | if self.parent.parent.right.count < self.count: 93 | root = self.parent.rotate_right(root) 94 | rotates +=1 95 | moreRotates, root = self.parent.checkForRotates(root) 96 | return rotates + moreRotates, root 97 | else: 98 | return rotates, root 99 | 100 | def delete(self, data, root): 101 | """ 102 | Delete node containing data 103 | 104 | @param data node's content to delete 105 | """ 106 | # get node containing data 107 | node, parent, count = self.lookup(data) 108 | rotates = 0 109 | string = str(node) 110 | if string != "None": 111 | children_count = node.children_count() 112 | if children_count == 0: 113 | node.parent.subtractOneFromParents() 114 | rotates, root = node.parent.checkForRotates(root) 115 | # if node has no children, just remove it 116 | if parent.left is node: 117 | parent.left = None 118 | else: 119 | parent.right = None 120 | del node 121 | elif children_count == 1: 122 | node.parent.subtractOneFromParents() 123 | rotates, root = node.parent.checkForRotates(root) 124 | # if node has 1 child 125 | # replace node by its child 126 | if node.left: 127 | n = node.left 128 | else: 129 | n = node.right 130 | if parent: 131 | if parent.left is node: 132 | parent.left = n 133 | else: 134 | parent.right = n 135 | del node 136 | else: 137 | # if node has 2 children 138 | # find its successor 139 | parent = node 140 | successor = node.right 141 | while successor.left: 142 | parent = successor 143 | successor = successor.left 144 | # replace node data by its successor data 145 | node.data = successor.data 146 | # fix successor's parent node child 147 | if parent.left == successor: 148 | parent.left = successor.right 149 | else: 150 | parent.right = successor.right 151 | parent.subtractOneFromParents() 152 | rotates, root = parent.checkForRotates(root) 153 | return count, rotates, root 154 | else: 155 | return 0, 0, root 156 | 157 | def compare_trees(self, node): 158 | """ 159 | Compare 2 trees 160 | 161 | @param node tree to compare 162 | @returns True if the tree passed is identical to this tree 163 | """ 164 | if node is None: 165 | return False 166 | if self.data != node.data: 167 | return False 168 | res = True 169 | if self.left is None: 170 | if node.left: 171 | return False 172 | else: 173 | res = self.left.compare_trees(node.left) 174 | if res is False: 175 | return False 176 | if self.right is None: 177 | if node.right: 178 | return False 179 | else: 180 | res = self.right.compare_trees(node.right) 181 | return res 182 | 183 | def print_tree(self): 184 | """ 185 | Print tree content inorder 186 | """ 187 | if self.left: 188 | self.left.print_tree() 189 | print self.data, 190 | if self.right: 191 | self.right.print_tree() 192 | 193 | def print_tree2(self): 194 | """ 195 | Print tree content in tree order 196 | """ 197 | print self.data, 198 | print ' l ', 199 | if self.left: 200 | self.left.print_tree2() 201 | print ' r ', 202 | if self.right: 203 | self.right.print_tree2() 204 | 205 | def tree_data(self): 206 | """ 207 | Generator to get the tree nodes data 208 | """ 209 | # we use a stack to traverse the tree in a non-recursive way 210 | stack = [] 211 | node = self 212 | while stack or node: 213 | if node: 214 | stack.append(node) 215 | node = node.left 216 | else: # we are returning so we pop the node and we yield it 217 | node = stack.pop() 218 | yield node.data 219 | node = node.right 220 | 221 | def children_count(self): 222 | """ 223 | Return the number of children 224 | 225 | @returns number of children: 0, 1, 2 226 | """ 227 | cnt = 0 228 | if self.left: 229 | cnt += 1 230 | if self.right: 231 | cnt += 1 232 | return cnt 233 | 234 | # def rotate_right(self, parent, root): 235 | def rotate_right(self, root): 236 | """ 237 | During the right rotation: 238 | a is decreased by b-e, 239 | b is increased by a-b, 240 | c, d, e, f, g are left alone 241 | """ 242 | parent = self.parent 243 | if self.right != None: 244 | oldCount = self.count - self.right.count 245 | self.right.parent = parent 246 | else: 247 | oldCount = self.count 248 | self.count += (parent.count - self.count) 249 | parent.count -= oldCount 250 | parent.left = self.right; 251 | self.parent = parent.parent 252 | if parent.parent != None: 253 | if parent.parent.left == parent: 254 | parent.parent.left = self 255 | else: 256 | parent.parent.right = self 257 | self.right = parent; 258 | parent.parent = self 259 | if root == parent: 260 | return self; 261 | return root; 262 | 263 | 264 | # def rotate_left(self, parent, root): 265 | def rotate_left(self, root): 266 | """ 267 | During the left rotation: 268 | a is decreased by c-f, 269 | c is increased by a-c, 270 | b, d, e, f, and g are left alone. 271 | """ 272 | parent = self.parent 273 | if self.left != None: 274 | oldCount = self.count - self.left.count 275 | self.left.parent = parent 276 | else: 277 | oldCount = self.count 278 | self.count += (parent.count - self.count) 279 | parent.count -= oldCount 280 | parent.right = self.left 281 | self.parent = parent.parent 282 | if parent.parent != None: 283 | if parent.parent.left == parent: 284 | parent.parent.left = self 285 | else: 286 | parent.parent.right = self 287 | parent.parent = self 288 | self.left = parent 289 | if root == parent: 290 | return self; 291 | return root; -------------------------------------------------------------------------------- /Algorithms/MidtermCode/binary_tree.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calvint/AlgorithmsOneProblems/945ea58295d277e484dfd4f5cc2cac033ccd2b2b/Algorithms/MidtermCode/binary_tree.pyc -------------------------------------------------------------------------------- /Algorithms/MidtermCode/pageTwo.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calvint/AlgorithmsOneProblems/945ea58295d277e484dfd4f5cc2cac033ccd2b2b/Algorithms/MidtermCode/pageTwo.JPG -------------------------------------------------------------------------------- /Algorithms/MidtermCode/red_black_set_mod.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calvint/AlgorithmsOneProblems/945ea58295d277e484dfd4f5cc2cac033ccd2b2b/Algorithms/MidtermCode/red_black_set_mod.pyc -------------------------------------------------------------------------------- /Algorithms/MidtermCode/test_binary_tree.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import binary_tree as binary_tree 3 | 4 | class BinaryTreeTest(unittest.TestCase): 5 | 6 | def test_binary_tree(self): 7 | 8 | data = [10, 5, 15, 4, 7, 13, 17, 11, 14] 9 | # create 2 trees with the same content 10 | root = binary_tree.Node(data[0]) 11 | for i in data[1:]: 12 | root.insert(i, root) 13 | 14 | root2 = binary_tree.Node(data[0]) 15 | for i in data[1:]: 16 | root2.insert(i, root2) 17 | 18 | # check if both trees are identical 19 | self.assertTrue(root.compare_trees(root2)) 20 | 21 | # check the content of the tree inorder 22 | t = [] 23 | for d in root.tree_data(): 24 | t.append(d) 25 | self.assertEquals(t, [4, 5, 7, 10, 11, 13, 14, 15, 17]) 26 | 27 | root.print_tree2() 28 | 29 | # test lookup 30 | node, parent, compares = root.lookup(9) 31 | self.assertTrue(node is None) 32 | # check if returned node and parent are correct 33 | node, parent, compares = root.lookup(11) 34 | self.assertTrue(node.data == 11) 35 | self.assertTrue(parent.data == 13) 36 | 37 | # delete a leaf node 38 | root.delete(4, root) 39 | 40 | root.print_tree2() 41 | # check the content of the tree inorder 42 | t = [] 43 | for d in root.tree_data(): 44 | t.append(d) 45 | self.assertEquals(t, [5, 7, 10, 11, 13, 14, 15, 17]) 46 | 47 | # delete a node with 1 child 48 | root.delete(5, root) 49 | # check the content of the tree inorder 50 | t = [] 51 | for d in root.tree_data(): 52 | t.append(d) 53 | self.assertEquals(t, [7, 10, 11, 13, 14, 15, 17]) 54 | 55 | # delete a node with 2 children 56 | root.delete(13, root) 57 | # check the content of the tree inorder 58 | t = [] 59 | for d in root.tree_data(): 60 | t.append(d) 61 | self.assertEquals(t, [7, 10, 11, 14, 15, 17]) 62 | 63 | # delete a node with 2 children 64 | root.delete(15, root) 65 | # check the content of the tree inorder 66 | t = [] 67 | for d in root.tree_data(): 68 | t.append(d) 69 | self.assertEquals(t, [7, 10, 11, 14, 17]) 70 | 71 | root.print_tree2() 72 | print '\n' 73 | 74 | root = root.right.rotate_left(root) 75 | root.print_tree2() 76 | print '\n' 77 | 78 | root = root.left.rotate_right(root) 79 | root.print_tree2() 80 | print '\n' 81 | 82 | 83 | if __name__ == '__main__': 84 | unittest.main() -------------------------------------------------------------------------------- /Algorithms/SortHomework/bubble_insert_merge_sort.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Created on Aug 12, 2014 3 | 4 | @author: Gary, and now Calvin too! 5 | ''' 6 | 7 | import time 8 | import quickSort 9 | import heapSort 10 | from copy import deepcopy 11 | 12 | 13 | def sort_me_merge(arr,p,r): 14 | # This is a merge sort 15 | if ( p < r ): 16 | q = (p + r) / 2 17 | sort_me_merge( arr,p ,q) 18 | sort_me_merge( arr,q+1,r) 19 | 20 | #merge 21 | p2 = p 22 | q2 = q+1 23 | arrTemp = list(arr[p:r+1]) 24 | i = 0 25 | while ( p2 <= q and q2 <= r ): 26 | if ( arr[p2] > arr[q2] ): # then swap 27 | arrTemp[i] = arr[q2] 28 | q2 = q2 + 1 29 | else: 30 | arrTemp[i] = arr[p2] 31 | p2 = p2 + 1 32 | i = i + 1 33 | if ( p2 <= q ): 34 | arrTemp[r-p-q+p2:r-p+1] = arr[p2:q+1] 35 | arr[p:r+1] = list(arrTemp) 36 | 37 | def sort_me_insertion(arr): 38 | # This is an insertion sort 39 | for j in range(1,len(arr)): 40 | key = arr[j] 41 | i = j - 1 42 | while ( i>=0 and arr[i]>key ): 43 | arr[i+1] = arr[i] 44 | i = i-1 45 | arr[i+1] = key 46 | 47 | def sort_me_bubble(arr): 48 | # This is a bubble sort 49 | for i in range(0,len(arr)-1): 50 | for j in range(i+1,len(arr)): 51 | if arr[i] > arr[j]: # if TRUE then swap i and j in array 52 | temp = arr[i] 53 | arr[i] = arr[j] 54 | arr[j] = temp 55 | 56 | def time_me(method): 57 | def wrapper(*args, **kw): 58 | startTime = int(round(time.time() * 1000)) 59 | result = method(*args, **kw) 60 | endTime = int(round(time.time() * 1000)) 61 | 62 | #print(endTime - startTime,'ms') 63 | return [result,endTime-startTime] 64 | 65 | return wrapper 66 | 67 | def is_sorted(lst, key=lambda x, y: x < y): 68 | for i, el in enumerate(lst[1:]): 69 | if key(el, lst[i]): 70 | return False 71 | return True 72 | 73 | def generate_shuffled_array(nElements,isPrint=False): 74 | import numpy as np 75 | arr = np.arange(nElements) 76 | if isPrint: print(arr) 77 | np.random.shuffle(arr) 78 | if isPrint: print(arr) 79 | return arr 80 | 81 | @time_me 82 | def sort_me_timed_merge(arr): 83 | sort_me_merge_complete(arr) 84 | 85 | def sort_me_merge_complete(arr): 86 | sort_me_merge(arr,0,len(arr)-1) 87 | 88 | @time_me 89 | def sort_me_timed_bubble(arr): 90 | sort_me_bubble(arr) 91 | 92 | @time_me 93 | def sort_me_timed_insert(arr): 94 | sort_me_insertion(arr) 95 | 96 | @time_me 97 | def sort_me_timed_heap(arr): 98 | heapSort.heapSort(heapSort.makeHeap(arr)) 99 | 100 | @time_me 101 | def sort_me_timed_quick(arr): 102 | quickSort.quicksort(arr, 0, len(arr) -1 ) 103 | 104 | nElements = 100 105 | print("_________________________________________________") 106 | print("|n |merge |bubble |insert |heap |quick |") 107 | print("-------------------------------------------------") 108 | for i in range(0,6): 109 | arr = generate_shuffled_array(nElements,False) 110 | """wrapers are cool, I'm definitely going to use them in the future!""" 111 | merge_time = sort_me_timed_merge(deepcopy(arr))[1] 112 | bubble_time = sort_me_timed_bubble(deepcopy(arr))[1] 113 | insert_time = sort_me_timed_insert(deepcopy(arr))[1] 114 | heap_time = sort_me_timed_heap(arr)[1] 115 | quick_time = sort_me_timed_quick(arr)[1] 116 | print("|%7d|%7d|%7d|%7d|%7d|%7d|" % (nElements, merge_time, bubble_time, insert_time, heap_time, quick_time)) 117 | print("-------------------------------------------------") 118 | nElements *= 2; 119 | 120 | 121 | -------------------------------------------------------------------------------- /Algorithms/SortHomework/bubble_insert_merge_sort.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calvint/AlgorithmsOneProblems/945ea58295d277e484dfd4f5cc2cac033ccd2b2b/Algorithms/SortHomework/bubble_insert_merge_sort.pyc -------------------------------------------------------------------------------- /Algorithms/SortHomework/heapSort.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | """Heapsort Workshop 4 | Calvin Troutt 9/7/14 5 | """ 6 | def makeHeap(array): 7 | result = {} 8 | index = 0 9 | for number in array: 10 | index += 1 11 | result[index] = number 12 | return result 13 | 14 | def maxHeapify(heap, index): 15 | left = index << 1 16 | right = (index << 1) +1 17 | if left <= len(heap) and heap[left] > heap[index]: 18 | largest = left 19 | else: 20 | largest = index 21 | if right <= len(heap) and heap[right] > heap[largest]: 22 | largest = right 23 | if largest != index: 24 | tempVar = heap[index] 25 | heap[index] = heap[largest] 26 | heap[largest] = tempVar 27 | maxHeapify(heap, largest) 28 | 29 | def buildMaxHeap(heap): 30 | for i in range(len(heap)/2, 0, -1): 31 | maxHeapify(heap, i) 32 | 33 | def heapSort(heap): 34 | buildMaxHeap(heap) 35 | result = [] 36 | for i in range(len(heap), 1, -1): 37 | result.append(heap[1]) 38 | heap[1] = heap[i] 39 | del heap[i] 40 | maxHeapify(heap, 1) 41 | result.append(heap[1]) 42 | return result 43 | 44 | # heap = makeHeap([3,14,5,76,24,2]) 45 | # result = heapSort(heap) 46 | # for i in result: 47 | # print(i) 48 | 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /Algorithms/SortHomework/heapSort.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calvint/AlgorithmsOneProblems/945ea58295d277e484dfd4f5cc2cac033ccd2b2b/Algorithms/SortHomework/heapSort.pyc -------------------------------------------------------------------------------- /Algorithms/SortHomework/quickSort.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Created on Sep 14, 2014 3 | 4 | @author: Calvin 5 | ''' 6 | 7 | def quicksort(array, start_index, end_index ): 8 | if start_index < end_index: 9 | q = partition(array, start_index, end_index) 10 | quicksort(array, start_index, q - 1) 11 | quicksort(array, q +1, end_index) 12 | 13 | def exchange_items(array, first_index, second_index): 14 | x = array[second_index ] 15 | array[second_index] = array[first_index] 16 | array[first_index] = x 17 | 18 | def partition(array, start_index, end_index): 19 | x = array[end_index] 20 | i = start_index -1; 21 | for j in range(start_index, end_index): 22 | if array[j] <= x: 23 | i = i + 1 24 | exchange_items(array, i, j) 25 | exchange_items(array, i +1, end_index) 26 | return i +1 27 | 28 | # array = [3,14,5,76,24,2] 29 | # quicksort(array, 0, 5); 30 | # for i in array: 31 | # print(i) 32 | 33 | 34 | -------------------------------------------------------------------------------- /Algorithms/SortHomework/quickSort.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calvint/AlgorithmsOneProblems/945ea58295d277e484dfd4f5cc2cac033ccd2b2b/Algorithms/SortHomework/quickSort.pyc -------------------------------------------------------------------------------- /Algorithms/Thomas H. Cormen, Charles E. Leiserson, Ronald L. Rivest, Clifford Stein Introduction to Algorithms, Third Edition 2009.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calvint/AlgorithmsOneProblems/945ea58295d277e484dfd4f5cc2cac033ccd2b2b/Algorithms/Thomas H. Cormen, Charles E. Leiserson, Ronald L. Rivest, Clifford Stein Introduction to Algorithms, Third Edition 2009.pdf -------------------------------------------------------------------------------- /Algorithms/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calvint/AlgorithmsOneProblems/945ea58295d277e484dfd4f5cc2cac033ccd2b2b/Algorithms/__init__.py -------------------------------------------------------------------------------- /Algorithms/algorithmsSolutions.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calvint/AlgorithmsOneProblems/945ea58295d277e484dfd4f5cc2cac033ccd2b2b/Algorithms/algorithmsSolutions.pdf -------------------------------------------------------------------------------- /Algorithms/bst/Hash.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Created on Sep 22, 2014 3 | 4 | @author: Calvin 5 | ''' 6 | import random 7 | from _ast import Num 8 | 9 | 10 | 11 | #class implementing linked list 12 | class Stack(object): 13 | 14 | itemArray = None 15 | 16 | def __init__(self): 17 | self.itemArray = [] 18 | 19 | def push(self, int): 20 | self.itemArray.append(int) 21 | 22 | def pop(self): 23 | return self.pop() 24 | 25 | def search(self, int): 26 | count = 0 27 | searched = [] 28 | while self.itemArray != []: 29 | num = self.itemArray.pop() 30 | searched.append(num) 31 | count += 1 32 | if num == int: 33 | for i in range(len(searched)): 34 | self.itemArray.append(searched.pop()) 35 | return (True, count) 36 | for i in range(len(searched)): 37 | self.itemArray.append(searched.pop()) 38 | return (False, count) 39 | 40 | def delete(self, int): 41 | count = 0 42 | searched = [] 43 | while self.itemArray != []: 44 | num = self.itemArray.pop() 45 | searched.append(num) 46 | count += 1 47 | if num == int: 48 | searched.pop() 49 | for i in range(len(searched)): 50 | self.itemArray.append(searched.pop()) 51 | return (True, count) 52 | for i in range(len(searched)): 53 | self.itemArray.append(searched.pop()) 54 | return (False, count) 55 | 56 | 57 | #takes a dictionary as the hash table and a list of numbers that 58 | # must be added to the hash 59 | class HashTable(): 60 | table = None 61 | divCount = None 62 | 63 | def __init__(self, divisions): 64 | self.table = {} 65 | self.divCount = divisions 66 | 67 | def insert(self, keys): 68 | for num in keys: 69 | mod = num%100000 70 | if mod not in self.table.keys(): 71 | self.table[mod] = Stack() 72 | self.table[mod].push(num) 73 | elif mod in self.table.keys(): 74 | self.table[mod].push(num) 75 | 76 | def search(self, keys): 77 | results = [] 78 | count = 0 79 | for num in keys: 80 | mod = num%100000 81 | if mod in self.table.keys(): 82 | resultTuple = self.table[mod].search(num) 83 | count += resultTuple[1] 84 | if resultTuple[0]: 85 | results.append(num) 86 | 87 | return results, count 88 | 89 | def delete(self, keys): 90 | results = [] 91 | count = 0 92 | for num in keys: 93 | mod = num%100000 94 | if mod in self.table.keys(): 95 | resultTuple = self.table[mod].delete(num) 96 | count += resultTuple[1] 97 | if resultTuple[0]: 98 | results.append(num) 99 | return results, count 100 | 101 | 102 | def generateKeys(number, domain): 103 | keys = [] 104 | for i in range(number): 105 | keys.append(random.randrange(0,domain)) 106 | return keys 107 | 108 | def generateSmallKeys(): 109 | keys = [] 110 | for i in range(0, 10): 111 | keys.append(random.randrange(0,20)) 112 | return keys 113 | 114 | if __name__ == "__main__": 115 | hashtb = HashTable(100000) 116 | keys_one = generateKeys(10000, 2000000000) 117 | hashtb.insert(keys_one) 118 | keys_two = generateKeys(10000, 2000000000) 119 | results = hashtb.search(keys_two) 120 | print "search found this many matches: {0} \n".format(len(results[0])) 121 | print "list of common keys from search: " + str(list( set(keys_one).intersection( set(keys_two) ) ) ) 122 | print "number of comparisons: " + str(results[1]) 123 | 124 | keys_three = generateKeys(10000, 2000000000) 125 | results = hashtb.delete(keys_three) 126 | 127 | print "delete found this many matches: {0} \n".format(len(results[0])) 128 | print "list of common keys from delete: " + str(list( set(keys_one).intersection( set(keys_three) ) ) ) 129 | print "number of comparisons: " + str(results[1]) 130 | 131 | 132 | 133 | 134 | -------------------------------------------------------------------------------- /Algorithms/bst/Main.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Created on Oct 2, 2014 3 | 4 | @author: Calvin 5 | ''' 6 | import red_black_set_mod 7 | import Hash 8 | 9 | 10 | keys = Hash.generateKeys(10000, 2000000000) 11 | #case (i)... 12 | table_one = Hash.HashTable(100000) 13 | #case (ii)... 14 | table_two = Hash.HashTable(1000) 15 | #red black BST 16 | rb_tree = red_black_set_mod.RedBlackTree() 17 | 18 | #a) the inserts 19 | table_one.insert(keys) 20 | print "inserted 10,000 elements into table_one with 0 comparisons\n" 21 | 22 | table_two.insert(keys) 23 | print "inserted 10,000 elements into table_two with 0 comparisons\n" 24 | 25 | totalCount = 0 26 | for key in keys: 27 | node, count = rb_tree.add(key) 28 | totalCount += count 29 | print "inserted 10,000 elements into rb_tree with {0} comparisons\n".format(str(totalCount)) 30 | 31 | #b) the deletes 32 | 33 | #(i) deleting first 1000 elements 34 | results, count = table_one.delete(keys[9000:9999]) 35 | print "deleted last 1000 elements added to table_one with {0} comparisons\n".format(count) 36 | 37 | results, count = table_one.delete(keys[9000:9999]) 38 | print "deleted last 1000 elements added to table_two with {0} comparisons\n".format(count) 39 | 40 | totalCount = 0 41 | for key in keys[9000:9999]: 42 | count = rb_tree.discard(key) 43 | totalCount += count 44 | print "deleted last 1000 elements added to rb_tree with {0} comparisons\n".format(totalCount) 45 | 46 | #(i) deleting first 100 elements 47 | 48 | #re-inserting all of the keys to delete the first 100 49 | table_one = Hash.HashTable(100000) 50 | table_two = Hash.HashTable(1000) 51 | rb_tree = red_black_set_mod.RedBlackTree() 52 | table_one.insert(keys) 53 | table_two.insert(keys) 54 | totalCount = 0 55 | for key in keys: 56 | node, count = rb_tree.add(key) 57 | totalCount += count 58 | 59 | #actually deleting stuff 60 | results, count = table_one.delete(keys[9900:9999]) 61 | print "deleted last 100 elements added to table_one with {0} comparisons\n".format(count) 62 | 63 | results, count = table_one.delete(keys[9900:9999]) 64 | print "deleted last 100 elements added to table_two with {0} comparisons\n".format(count) 65 | 66 | totalCount = 0 67 | for key in keys[9900:9999]: 68 | count = rb_tree.discard(key) 69 | totalCount += count 70 | print "deleted last 100 elements added to rb_tree with {0} comparisons\n".format(totalCount) 71 | 72 | -------------------------------------------------------------------------------- /Algorithms/bst/bst.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calvint/AlgorithmsOneProblems/945ea58295d277e484dfd4f5cc2cac033ccd2b2b/Algorithms/bst/bst.zip -------------------------------------------------------------------------------- /Algorithms/bst/output.txt: -------------------------------------------------------------------------------- 1 | inserted 10,000 elements into table_one with 0 comparisons 2 | 3 | inserted 10,000 elements into table_two with 0 comparisons 4 | 5 | inserted 10,000 elements into rb_tree with 121347 comparisons 6 | 7 | deleted last 1000 elements added to table_one with 1003 comparisons 8 | 9 | deleted last 1000 elements added to table_two with 86 comparisons 10 | 11 | deleted last 1000 elements added to rb_tree with 14775 comparisons 12 | 13 | deleted last 100 elements added to table_one with 99 comparisons 14 | 15 | deleted last 100 elements added to table_two with 16 comparisons 16 | 17 | deleted last 100 elements added to rb_tree with 1470 comparisons 18 | -------------------------------------------------------------------------------- /Algorithms/bst/red_black_dict_mod.py: -------------------------------------------------------------------------------- 1 | 2 | # pylint: disable=C0302 3 | # C0302: Unfortunately, we want a lot of lines in this module 4 | 5 | # duplicate-code: We m4 preprocess this file into 2 or more versions, so 6 | # of course we have "duplicate" code. But things aren't duplicated in 7 | # the m4. Unfortunately, pylint doesn't allow us to override this 8 | # one, so we use a regex to this-pylint in addition to this disable. 9 | # too-many-lines: We want something self-contained, so this has a lot of 10 | # lines 11 | 12 | '''Red-Black tree and plain Binary Tree module''' 13 | 14 | ##Copyright (c) 2013 duncan g. smith and Dan Stromberg 15 | ## 16 | ##(This is the well-known MIT license) 17 | ## 18 | ##Permission is hereby granted, free of charge, to any person obtaining a 19 | ##copy of this software and associated documentation files (the "Software"), 20 | ##to deal in the Software without restriction, including without limitation 21 | ##the rights to use, copy, modify, merge, publish, distribute, sublicense, 22 | ##and/or sell copies of the Software, and to permit persons to whom the 23 | ##Software is furnished to do so, subject to the following conditions: 24 | ## 25 | ##The above copyright notice and this permission notice shall be included 26 | ##in all copies or substantial portions of the Software. 27 | ## 28 | ##THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 29 | ##OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 30 | ##FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 31 | ##THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 32 | ##OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 33 | ##ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 34 | ##OTHER DEALINGS IN THE SOFTWARE. 35 | 36 | # This code was originally by Duncan G. Smith, but has been modified a 37 | # bit by Dan Stromberg. 38 | 39 | 40 | 41 | from __future__ import division 42 | 43 | import sys 44 | import math 45 | import random 46 | import itertools 47 | 48 | if hasattr(itertools, 'izip_longest'): 49 | MY_ZIP_LONGEST = getattr(itertools, 'izip_longest') 50 | else: 51 | MY_ZIP_LONGEST = getattr(itertools, 'zip_longest') 52 | 53 | try: 54 | next 55 | except NameError: 56 | def next(iterator): 57 | # pylint: disable=redefined-builtin 58 | '''A version of next() for python's that don't have it''' 59 | return iterator.next() 60 | 61 | #class TreeError(Exception): 62 | # '''An exception to raise if the tree gets in a bad state - unused''' 63 | # pass 64 | 65 | def center(string, field_use_width, field_avail_width): 66 | '''Center a string within a given field width''' 67 | field_use = (string + '_' * (field_use_width - 1))[:field_use_width - 1] 68 | pad_width = (field_avail_width - len(field_use)) / 2.0 69 | result = ' ' * int(pad_width) + field_use + ' ' * int(math.ceil(pad_width)) 70 | return result 71 | 72 | class BinaryTree(object): 73 | # pylint: disable=too-many-public-methods,incomplete-protocol 74 | """ 75 | A basic binary tree class. Arbitrary data can be 76 | associated with a tree. A tree that is root has 77 | parent equal to None; a tree that is leaf has left 78 | and right children that are empty trees (sentinels). 79 | An empty tree has left and right children equal to None. 80 | 81 | A tree can have children (or a parent) with equal 82 | data. 83 | """ 84 | 85 | __slots__ = [ 'key', 'value', 'left', 'right', 'parent' ] 86 | 87 | def __init__(self, key=None, value=None, left=None, right=None, parent=None): 88 | """ 89 | Initialises instance. 90 | 91 | @type key: arbitrary type 92 | @param key: data 93 | @type value: arbitrary type 94 | @param value: data 95 | @type left: L{BinaryTree} or C{None} 96 | @param left: left child 97 | @type right: L{BinaryTree} or C{None} 98 | @param right: right child 99 | @type parent: L{BinaryTree} or C{None} 100 | @param parent: parent 101 | """ 102 | self.key = key 103 | self.value = value 104 | self.left = left 105 | self.right = right 106 | self.parent = parent 107 | 108 | def __repr__(self): 109 | """ 110 | Returns a string representation of I{self}, containing 111 | the key in I{self} and left and right children. If I{self} 112 | is a sentinel the string "sentinel" is returned. 113 | 114 | @rtype: C{str} 115 | @return: string representation 116 | """ 117 | if self: 118 | return '%s %s' % (self.key, self.value) 119 | else: 120 | return "sent" 121 | 122 | def _depth_and_field_width(self): 123 | '''Compute the depth of the tree and the maximum width within the nodes - for pretty printing''' 124 | class maxer(object): 125 | '''Class facilitates computing the max depth of the treap (tree) and max width of the nodes''' 126 | def __init__(self, maximum=-1): 127 | self.depth_max = maximum 128 | self.field_width_max = -1 129 | 130 | def feed(self, node, key, value, depth, from_left): 131 | '''Check our maximums so far against the current node - updating as needed''' 132 | # pylint: disable=R0913 133 | # R0913: We need a bunch of arguments 134 | dummy = key 135 | dummy = value 136 | dummy = from_left 137 | if depth > self.depth_max: 138 | self.depth_max = depth 139 | repr_node = repr(node) 140 | len_node = len(repr_node) 141 | if len_node > self.field_width_max: 142 | self.field_width_max = len_node 143 | 144 | def result(self): 145 | '''Return the maximums we've computed''' 146 | return (self.depth_max + 1, self.field_width_max) 147 | 148 | max_obj = maxer() 149 | self.detailed_inorder_traversal(max_obj.feed) 150 | return max_obj.result() 151 | 152 | def to_dot(self, initial=True, file_=sys.stdout, visited=None): 153 | """Generate a dot file describing this tree""" 154 | 155 | if visited is None: 156 | visited = set() 157 | if initial: 158 | file_.write('digraph G {\n') 159 | this_node = '%s %s' % (id(self), repr(self)) 160 | if this_node in visited: 161 | return 162 | visited.add(this_node) 163 | if self.left is not None: 164 | file_.write(' "%s" -> "%s %s" [ label="left" ]\n' % (this_node, id(self.left), repr(self.left))) 165 | self.left.to_dot(initial=False, file_=file_, visited=visited) 166 | if self.right is not None: 167 | file_.write(' "%s" -> "%s %s" [ label="right" ]\n' % (this_node, id(self.right), repr(self.right))) 168 | self.right.to_dot(initial=False, file_=file_, visited=visited) 169 | if initial: 170 | file_.write('}\n') 171 | 172 | def __str__(self): 173 | '''Format a tree as a string''' 174 | class Desc(object): 175 | # pylint: disable=R0903 176 | # R0903: We don't need a lot of public methods 177 | '''Build a pretty-print string during a recursive traversal''' 178 | def __init__(self, pretree): 179 | self.tree = pretree 180 | 181 | def update(self, node, key, value, depth, from_left): 182 | '''Add a node to the tree''' 183 | # pylint: disable=R0913 184 | # R0913: We need a bunch of arguments 185 | dummy = key 186 | dummy = value 187 | self.tree[depth][from_left] = repr(node) 188 | 189 | pretree = [] 190 | depth, field_use_width = self._depth_and_field_width() 191 | field_use_width += 1 192 | for level in range(depth): 193 | string = '_' * (field_use_width - 1) 194 | pretree.append([string] * 2 ** level) 195 | desc = Desc(pretree) 196 | self.detailed_inorder_traversal(desc.update) 197 | result = [] 198 | widest = 2 ** (depth - 1) * field_use_width 199 | for level in range(depth): 200 | two_level = 2 ** level 201 | field_avail_width = widest / two_level 202 | string = ''.join([center(x, field_use_width, field_avail_width) for x in desc.tree[level]]) 203 | # this really isn't useful for more than dozen values or so 204 | result.append(('%2d ' % level) + string) 205 | return '\n'.join(result) 206 | 207 | def __nonzero__(self): 208 | """ 209 | A sentinel node evaluates to False. 210 | 211 | @rtype: C{bool} 212 | @return: True if neither left nor right are None, 213 | otherwise False 214 | """ 215 | return not (self.left is None and self.right is None) 216 | 217 | __bool__ = __nonzero__ 218 | 219 | @property 220 | def is_root(self): 221 | """ 222 | Returns True if I{self} is root. 223 | 224 | @rtype: C{bool} 225 | @return: True if I{self} is root, otherwise False 226 | """ 227 | return self.parent is None 228 | 229 | @property 230 | def sibling(self): 231 | """ 232 | Returns the sibling of I{self}. 233 | 234 | @rtype: L{BinaryTree} or C{None} 235 | @return: sibling 236 | """ 237 | # sibling may be a sentinel node 238 | if self.is_root: 239 | return None 240 | elif self is self.parent.left: 241 | return self.parent.right 242 | else: 243 | return self.parent.left 244 | 245 | @property 246 | def size(self): 247 | """ 248 | Return the number of key items / non-sentinel 249 | nodes in the subtree rooted at I{self}. 250 | 251 | @rtype: C{int} 252 | @return: number of elements 253 | """ 254 | if self: 255 | return self.left.size + self.right.size + 1 256 | return 0 257 | 258 | @property 259 | def num_levels(self): 260 | """ 261 | Returns the number of levels in the subtree 262 | rooted at I{self}. 263 | 264 | @rtype: C{int} 265 | @return: number of levels 266 | """ 267 | if self: 268 | return max(self.left.num_levels, self.right.num_levels) + 1 269 | return 0 270 | 271 | @property 272 | def height(self): 273 | """ 274 | Returns the height of the subtree rooted at 275 | I{self}. This is the number of levels - 1. 276 | 277 | @rtype: C{int} 278 | @return: number of levels 279 | """ 280 | return self.num_levels - 1 281 | 282 | def copy(self, parent=None): 283 | """ 284 | Returns a shallow copy of the subtree rooted 285 | at I{self} (does not copy data). 286 | 287 | @type parent: L{BinaryTree} or C{None} 288 | @param parent: parent 289 | @rtype: L{BinaryTree} 290 | @return: a copy 291 | """ 292 | if self: 293 | copy = self.__class__(key=self.key, value=self.value, parent=parent) 294 | copy.left = self.left.copy(copy) 295 | copy.right = self.right.copy(copy) 296 | else: 297 | copy = self.__class__(parent=parent) 298 | return copy 299 | 300 | # def copy(self, parent=None): 301 | # """ 302 | # Returns a shallow copy of the subtree rooted 303 | # at I{self} (does not copy data). 304 | # 305 | # @type parent: L{BinaryTree} or C{None} 306 | # @param parent: parent 307 | # @rtype: L{BinaryTree} 308 | # @return: a copy 309 | # """ 310 | # if self: 311 | # copy = self.__class__(self.data, parent=parent) 312 | # copy.left = self.left.copy(copy) 313 | # copy.right = self.right.copy(copy) 314 | # else: 315 | # copy = self.__class__(parent=parent) 316 | # return copy 317 | 318 | def del_node(self): 319 | """ 320 | Deletes a node from the subtree rooted at I{self}, 321 | but does not delete the subtree rooted at node. To 322 | delete a node on the basis of its data use the 323 | I{remove} method. 324 | """ 325 | if self.left: 326 | if self.right: 327 | # method chosen randomly in order to 328 | # prevent tree becoming too unbalanced 329 | if random.random() < 0.5: 330 | node = self.left.maximum 331 | self.key = node.key 332 | self.value = node.value 333 | node.del_node() 334 | else: 335 | node = self.right.minimum 336 | self.key = node.key 337 | self.value = node.value 338 | node.del_node() 339 | else: 340 | node = self.left 341 | self.key, self.left, self.right = node.key, node.left, node.right 342 | self.value = node.value 343 | self.left.parent = self 344 | self.right.parent = self 345 | elif self.right: 346 | node = self.right 347 | self.key, self.left, self.right = node.key, node.left, node.right 348 | self.value = node.value 349 | self.left.parent = self 350 | self.right.parent = self 351 | else: 352 | # make node a sentinel node 353 | self.key = self.left = self.right = None 354 | self.value = None 355 | 356 | def add(self, key, value): 357 | """ 358 | Adds a node containing I{key} to the subtree 359 | rooted at I{self}, returning the added node. 360 | 361 | @type key: arbitrary type 362 | @param key: key 363 | @type value: arbitrary type 364 | @param value: data 365 | @rtype: L{BinaryTree} 366 | @return: (replaced flag, added node) 367 | """ 368 | node = self.find(key) 369 | if not node: 370 | node.key = key 371 | node.value = value 372 | node.left, node.right = self.__class__(parent=node), self.__class__(parent=node) 373 | return (False, node) 374 | elif node.key == key: 375 | node.key = key 376 | node.value = value 377 | return (True, node) 378 | else: 379 | if random.random() < 0.5: 380 | return BinaryTree.add(node.left, key=key, value=value) 381 | else: 382 | return BinaryTree.add(node.right, key=key, value=value) 383 | 384 | def remove(self, key): 385 | """ 386 | Removes a node containing I{key} from the 387 | subtree rooted at I{self}, raising an error 388 | if the subtree does not contain I{key}. 389 | 390 | @type key: arbitrary type 391 | @param key: data 392 | @raise ValueError: if key is not contained in the 393 | subtree rooted at I{self} 394 | """ 395 | node = self.find(key) 396 | if node: 397 | node.del_node() 398 | else: 399 | raise ValueError('tree.remove(x): x not in tree') 400 | 401 | def discard(self, key): 402 | """ 403 | Discards a node containing I{key} from the subtree 404 | rooted at I{self} (without raising an error if the key 405 | is not present). 406 | 407 | @type key: arbitrary type 408 | @param key: data 409 | """ 410 | try: 411 | self.remove(key) 412 | except ValueError: 413 | pass 414 | 415 | def count(self, key): 416 | """ 417 | Returns the number of occurrences of I{key} in 418 | the subtree rooted at I{self}. 419 | 420 | @type key: arbitrary type 421 | @param key: data 422 | @rtype: C{int} 423 | @return: data count 424 | """ 425 | if self: 426 | node = self.find(key) 427 | if node: 428 | return node.left.count(key) + node.right.count(key) + 1 429 | return 0 430 | 431 | def __add__(self, iterable): 432 | """ 433 | Returns a tree with elements from the subtree 434 | rooted at I{self} and I{iterable}. I{iterable} 435 | may be any iterable, not necessarily another 436 | tree. 437 | 438 | @type iterable: any iterable type 439 | @param iterable: an iterable object 440 | @rtype: L{BinaryTree} 441 | @return: tree containing the elements in I{self} 442 | and iterable 443 | """ 444 | items = list(self) + list(iterable) 445 | random.shuffle(items) # try to avoid unbalanced tree 446 | tree = self.__class__() 447 | for item in items: 448 | tree.add(item) 449 | return tree 450 | 451 | def __iadd__(self, iterable): 452 | """ 453 | Adds the elements in I{iterable} to the 454 | subtree rooted at I{self}. 455 | 456 | @type iterable: any iterable type 457 | @param iterable: an iterable object 458 | """ 459 | items = list(iterable) 460 | random.shuffle(items) # try to avoid unbalanced tree 461 | for item in items: 462 | self.add(item) 463 | 464 | def find(self, key): 465 | """ 466 | Finds and returns a node containing I{key} in the 467 | subtree rooted at I{self}. If I{key} is not in 468 | the tree, then a sentinel in the location where 469 | I{key} can be inserted is returned. 470 | 471 | @type key: arbitrary type 472 | @param key: data 473 | @rtype: L{BinaryTree} 474 | @return: node containing key, or sentinel 475 | node 476 | """ 477 | if self: 478 | if self.key == key: 479 | return self 480 | elif key < self.key: 481 | return self.left.find(key) 482 | else: 483 | return self.right.find(key) 484 | return self 485 | 486 | @property 487 | def predecessor(self): 488 | """ 489 | Returns the predecessor of I{self}, or None if 490 | self has no predecessor. 491 | 492 | @rtype: L{BinaryTree} or None 493 | @return: predecessor of I{self} 494 | """ 495 | if self.left: 496 | return self.left.maximum 497 | else: 498 | if not self.parent: 499 | return None 500 | if self is self.parent.right: 501 | return self.parent 502 | else: 503 | current, parent = self, self.parent 504 | while parent and parent.left is current: 505 | current, parent = parent, parent.parent 506 | return parent 507 | 508 | @property 509 | def successor(self): 510 | """ 511 | Returns the successor of I{self}, or None if 512 | self has no successor. 513 | 514 | @rtype: L{BinaryTree} or None 515 | @return: successor of I{self} 516 | """ 517 | if self.right: 518 | return self.right.minimum 519 | else: 520 | if not self.parent: 521 | return None 522 | if self is self.parent.left: 523 | return self.parent 524 | else: 525 | current, parent = self, self.parent 526 | while parent and parent.right is current: 527 | current, parent = parent, parent.parent 528 | return parent 529 | 530 | def in_order(self): 531 | """ 532 | Returns a generator of nodes in the subtree 533 | rooted at I{self} in sorted order of node keys. 534 | 535 | @rtype: C{generator} 536 | @return: generator of sorted nodes 537 | """ 538 | if self: 539 | for node in self.left.in_order(): 540 | yield node 541 | yield self 542 | for node in self.right.in_order(): 543 | yield node 544 | 545 | def detailed_inorder_traversal(self, visit, depth=0, from_left=0): 546 | '''Do an inorder traversal - with lots of parameters''' 547 | if self.left: 548 | self.left.detailed_inorder_traversal(visit, depth + 1, from_left * 2) 549 | visit(self, self.key, self.value, depth, from_left) 550 | if self.right: 551 | self.right.detailed_inorder_traversal(visit, depth + 1, from_left * 2 + 1) 552 | 553 | def __iter__(self): 554 | """ 555 | Returns a generator of the node keys in 556 | the subtree rooted at I{self} in sorted 557 | order. 558 | 559 | @rtype: C{generator} 560 | @return: generator of sorted node values 561 | """ 562 | return (n.key for n in self.in_order()) 563 | 564 | def in_reverse(self): 565 | """ 566 | Returns a generator of nodes in the subtree 567 | rooted at I{self} in reverse sorted order 568 | of node keys. 569 | 570 | @rtype: C{generator} 571 | @return: generator of reverse sorted nodes 572 | """ 573 | if self: 574 | for node in self.right.in_reverse(): 575 | yield node 576 | yield self 577 | for node in self.left.in_reverse(): 578 | yield node 579 | 580 | def pre_order(self): 581 | """ 582 | Returns a generator of the nodes in the 583 | subtree rooted at I{self} in preorder. 584 | 585 | @rtype: C{generator} 586 | @return: generator of nodes in preorder 587 | """ 588 | if self: 589 | yield self 590 | for node in self.left.pre_order(): 591 | yield node 592 | for node in self.right.pre_order(): 593 | yield node 594 | 595 | def post_order(self): 596 | """ 597 | Returns a generator of the nodes in the 598 | subtree rooted at I{self} in postorder. 599 | 600 | @rtype: C{generator} 601 | @return: generator of nodes in postorder 602 | """ 603 | if self: 604 | for node in self.left.post_order(): 605 | yield node 606 | for node in self.right.post_order(): 607 | yield node 608 | yield self 609 | 610 | @property 611 | def minimum(self): 612 | """ 613 | Returns a node for which the node key is minimum 614 | in the subtree rooted at I{self}. Always returns 615 | the minimum-valued node with no left child. 616 | 617 | @rtype: L{BinaryTree} 618 | @return: node with minimum key value 619 | """ 620 | node = self 621 | while node.left: 622 | node = node.left 623 | return node 624 | 625 | @property 626 | def maximum(self): 627 | """ 628 | Returns a node for which the node key is maximum 629 | in the subtree rooted at I{self}. Always returns 630 | the maximum-valued node with no right child. 631 | 632 | @rtype: L{BinaryTree} 633 | @return: node with maximum key value 634 | """ 635 | node = self 636 | while node.right: 637 | node = node.right 638 | return node 639 | 640 | def is_similar(self, other): 641 | """ 642 | Two binary trees are similar if they have the 643 | same structure. 644 | 645 | @rtype: C{bool} 646 | @return: True if the subtrees rooted at I{self} 647 | and I{other} are similar, otherwise False 648 | """ 649 | if not self and not other: 650 | return True 651 | if self and other: 652 | return self.left.is_similar(other.left) and self.right.is_similar(other.right) 653 | return False 654 | 655 | def is_equivalent(self, other): 656 | """ 657 | Two binary trees are equivalent if they are similar 658 | and corresponding nodes contain the same keys. 659 | 660 | @rtype: C{bool} 661 | @return: True if the subtrees rooted at I{self} 662 | and I{other} are equivalent, otherwise False 663 | """ 664 | if not self and not other: 665 | return True 666 | if self and other: 667 | return (self.key == other.key and 668 | self.value == other.value and 669 | self.left.is_equivalent(other.left) and 670 | self.right.is_equivalent(other.right)) 671 | return False 672 | 673 | # def cmp(self, other): 674 | # """ 675 | # Compares I{self} with I{other} lexocographically. 676 | # 677 | # @type other: L{BinaryTree} 678 | # @param other: tree being compared to I{self} 679 | # @rtype: C{int} 680 | # @return: 0 if the subtrees rooted at I{self} 681 | # and I{other} contain equal values, 682 | # -1 if the ordered values in I{self} are lexicographically 683 | # less than the ordered values in I{other}, and 684 | # 1 if the ordered values in I{self} are lexicographically 685 | # greater than the ordered values in I{other} 686 | # """ 687 | # other_items = iter(other) 688 | # for self_item in self: 689 | # try: 690 | # other_item = next(other_items) 691 | # except StopIteration: 692 | # return 1 693 | # else: 694 | # if self_item < other_item: 695 | # return -1 696 | # elif self_item > other_item: 697 | # return 1 698 | # try: 699 | # next(other_items) 700 | # except StopIteration: 701 | # return 0 702 | 703 | def __cmp__(self, other): 704 | """ 705 | Returns C{0} if the subtrees rooted at 706 | I{self} and I{other} contain equal keys. 707 | I{other} need not be similar to I{self}. 708 | 709 | @type other: L{BinaryTree} 710 | @param other: tree being compared to I{self} 711 | @rtype: C{int} 712 | @return: 0 if the subtrees rooted at I{self} 713 | and I{other} do contain equal values, 714 | -1 if self < other. 715 | 1 if self > other. 716 | """ 717 | # FIXME: This probably should compare values too. 718 | for self_node, other_node in MY_ZIP_LONGEST(self.in_order(), other.in_order()): 719 | if self_node is None: 720 | return -1 721 | elif other_node is None: 722 | return 1 723 | elif self_node.key < other_node.key: 724 | return -1 725 | elif self_node.key > other_node.key: 726 | return 1 727 | return 0 728 | 729 | cmp = __cmp__ 730 | 731 | def __eq__(self, other): 732 | """ 733 | Returns C{True} if the subtrees rooted at 734 | I{self} and I{other} contain equal keys. 735 | I{other} need not be similar to I{self}. 736 | 737 | @type other: L{BinaryTree} 738 | @param other: tree being compared to I{self} 739 | @rtype: C{bool} 740 | @return: True if the subtrees rooted at I{self} 741 | and I{other} contain equal values, otherwise 742 | False 743 | """ 744 | return self.cmp(other) == 0 745 | 746 | def __neq__(self, other): 747 | """ 748 | Returns C{True} if the subtrees rooted at 749 | I{self} and I{other} do not contain equal keys. 750 | I{other} need not be similar to I{self}. 751 | 752 | @type other: L{BinaryTree} 753 | @param other: tree being compared to I{self} 754 | @rtype: C{bool} 755 | @return: True if the subtrees rooted at I{self} 756 | and I{other} do not contain equal values, 757 | otherwise False 758 | """ 759 | return self.cmp(other) != 0 760 | 761 | def __lt__(self, other): 762 | """ 763 | Returns C{True} if the subtrees rooted at 764 | I{self} and I{other} do not contain equal keys. 765 | I{other} need not be similar to I{self}. 766 | 767 | @type other: L{BinaryTree} 768 | @param other: tree being compared to I{self} 769 | @rtype: C{bool} 770 | @return: True if the subtrees rooted at I{self} 771 | and I{other} do not contain equal values, 772 | otherwise False 773 | """ 774 | return self.cmp(other) < 0 775 | 776 | def __gt__(self, other): 777 | return other.cmp(self) < 0 778 | 779 | def __le__(self, other): 780 | return self.cmp(other) <= 0 781 | 782 | def __ge__(self, other): 783 | return self.cmp(other) >= 0 784 | 785 | def __contains__(self, key): 786 | """ 787 | Returns true if I{key} is stored in any node in 788 | the subtree rooted at I{self}. 789 | 790 | @type key: arbitrary type 791 | @param key: data 792 | @rtype: C{bool} 793 | @return: True if I{key} in subtree rooted at 794 | I{self}, otherwise False 795 | """ 796 | return bool(self.find(key)) 797 | 798 | 799 | class RedBlackTree(BinaryTree): 800 | # pylint: disable=attribute-defined-outside-init,maybe-no-member,too-many-public-methods,incomplete-protocol 801 | """ 802 | A binary tree with an extra attribute, is_red. Colour (red 803 | or black) is used to maintain a reasonably balanced tree. 804 | Changes to the tree structure (rotations) are carried out 805 | in such a way that the name bound to the initial empty 806 | tree will always refer to the root node. The data stored 807 | in a given node may change, as well as its connectivity. 808 | e.g. 809 | 810 | >>> import trees 811 | >>> tree = trees.RedBlackTree() 812 | >>> tree.add(2) 813 | 2 False None None 814 | >>> tree.key # tree is bound to root node 815 | 2 816 | >>> tree.add(-1) 817 | -1 True None None 818 | >>> tree.add(1) # tree rotations are performed at this point 819 | 1 False -1 2 820 | >>> tree.key # the root node has changed (i.e. its key has been changed) 821 | 1 822 | >>> 823 | 824 | """ 825 | 826 | __slots__ = BinaryTree.__slots__ + [ 'is_red' ] 827 | 828 | def __init__(self, key=None, value=None, left=None, right=None, parent=None): 829 | """ 830 | Initialises instance. 831 | 832 | @type key: arbitrary type 833 | @param key: data 834 | @type left: L{RedBlackTree} or C{None} 835 | @param left: left child 836 | @type right: L{RedBlackTree} or C{None} 837 | @param right: right child 838 | @type parent: L{RedBlackTree} or C{None} 839 | @param parent: parent 840 | """ 841 | super(RedBlackTree, self).__init__(key, value, left, right, parent) 842 | self.is_red = False # default for sentinel nodes 843 | 844 | @property 845 | def is_black(self): 846 | """ 847 | Returns True if node colour is black. 848 | 849 | @rtype: C{bool} 850 | @return: True if node colour is black, otherwise False 851 | """ 852 | return not self.is_red 853 | 854 | @is_black.setter 855 | def is_black(self, val): 856 | """ 857 | Sets node colour. 858 | """ 859 | self.is_red = not val 860 | 861 | @property 862 | def black_height(self): 863 | """ 864 | @rtype: C{int} 865 | @return: black height of subtree rooted at I{self} 866 | """ 867 | if self.left: 868 | if self.left.is_black: 869 | return self.left.black_height + 1 870 | else: 871 | return self.left.black_height 872 | elif self.right: 873 | if self.right.is_black: 874 | return self.right.black_height + 1 875 | else: 876 | return self.right.black_height 877 | else: 878 | return 0 879 | 880 | def __repr__(self): 881 | """ 882 | Returns a string representation of I{self}, containing 883 | the key in I{self}, the colour, and the key in left and 884 | right children. If I{self} is a sentinel the string 885 | "sentinel" is returned. 886 | 887 | @rtype: C{str} 888 | @return: string representation 889 | """ 890 | if self: 891 | if self.is_red: 892 | color_string = 'red' 893 | else: 894 | color_string = 'blk' 895 | return '%s %s %s' % (self.key, self.value, color_string) 896 | else: 897 | return "sent" 898 | 899 | def copy(self, parent=None): 900 | """ 901 | Returns a shallow copy of the subtree rooted 902 | at I{self} (does not copy key). 903 | 904 | @type parent: L{RedBlackTree} or C{None} 905 | @param parent: parent 906 | @rtype: L{RedBlackTree} 907 | @return: a copy 908 | """ 909 | copy = super(RedBlackTree, self).copy(parent) 910 | copy.is_red = self.is_red 911 | return copy 912 | 913 | def add(self, key, value): 914 | """ 915 | Adds a node containing I{key} to the subtree 916 | rooted at I{self}, returning the node which 917 | contains I{key}. Note: the returned node might 918 | be an existing node due to key swaps on rotations. 919 | 920 | @type key: arbitrary type 921 | @param key: data 922 | @rtype: L{RedBlackTree} 923 | @return: node containing I{key} 924 | """ 925 | (replaced, node) = super(RedBlackTree, self).add(key=key, value=value) 926 | if not replaced: 927 | node.is_red = True 928 | node = node.fix_insert() 929 | return node 930 | 931 | def fix_insert(self): 932 | """ 933 | Carries out tree rotations, key swaps and recolourings 934 | to ensure that the red / black properties of the tree 935 | are preserved on node insertion. The returned node contains 936 | the same value as I{self} (but might not be the inserted node 937 | due to key swaps / rotations). 938 | 939 | @rtype: L{RedBlackTree} 940 | @return: node containing key originally 941 | contained in I{self} 942 | """ 943 | # case 1 944 | if self.parent is None: 945 | self.is_black = True 946 | return self 947 | # case 2 948 | if self.parent.is_black: 949 | return self 950 | # case 3 951 | sib = self.parent.sibling 952 | if sib.is_red: 953 | self.parent.is_black = True 954 | sib.is_black = True 955 | par = self.parent.parent 956 | par.is_red = True 957 | par.fix_insert() 958 | return self 959 | # cases 4 & 5 960 | par = self.parent.parent 961 | if not (self.parent.left is self) == (par.left is self.parent): 962 | self.rotate() 963 | self.parent.rotate() 964 | return par 965 | else: 966 | self.parent.rotate() 967 | return self 968 | 969 | def rotate(self): 970 | """ 971 | Rotates I{self} with I{self.parent}. Left / right rotations 972 | are performed as dictated by the parent-child relationship. 973 | The colours of I{self} and I{self.parent} are implicity swapped 974 | (as their values are swapped during the rotation). 975 | """ 976 | # data swapped and connections updated 977 | # colours are (implicitly) swapped 978 | if self is self.parent.left: 979 | self.key, self.parent.key = self.parent.key, self.key 980 | self.value, self.parent.value = self.parent.value, self.value 981 | self.parent.left = self.left 982 | self.parent.left.parent = self.parent 983 | self.left = self.right 984 | self.right = self.parent.right 985 | self.left.parent = self.right.parent = self 986 | self.parent.right = self 987 | else: 988 | self.key, self.parent.key = self.parent.key, self.key 989 | self.value, self.parent.value = self.parent.value, self.value 990 | self.parent.right = self.right 991 | self.parent.right.parent = self.parent 992 | self.right = self.left 993 | self.left = self.parent.left 994 | self.left.parent = self.right.parent = self 995 | self.parent.left = self 996 | 997 | def del_node(self): 998 | """ 999 | Deletes a node from the subtree rooted at I{self}, 1000 | but does not delete the subtree rooted at node. To 1001 | delete a node on the basis of its key use the 1002 | I{remove} method. 1003 | """ 1004 | # ensure that node being deleted has at most one child 1005 | if self.left: 1006 | if self.right: 1007 | # method chosen randomly in order to 1008 | # prevent tree becoming too unbalanced 1009 | if random.random() < 0.5: 1010 | node = self.left.maximum 1011 | else: 1012 | node = self.right.minimum 1013 | self.key, node.key = node.key, self.key 1014 | self.value, node.value = node.value, self.value 1015 | node.del_node() # node has at most one child 1016 | else: 1017 | node = self.left 1018 | self.key, self.left, self.right = node.key, node.left, node.right 1019 | self.value = node.value 1020 | self.left.parent = self # this might result in sentinel node's parent attribute being set 1021 | self.right.parent = self 1022 | self.is_black = True 1023 | elif self.right: 1024 | node = self.right 1025 | self.key, self.left, self.right = node.key, node.left, node.right 1026 | self.value = node.value 1027 | self.left.parent = self 1028 | self.right.parent = self 1029 | self.is_black = True 1030 | else: 1031 | # node being deleted is leaf 1032 | self.key = self.left = self.right = None 1033 | self.value = None 1034 | if self.is_red:# or self.sibling: # just make it a sentinel node 1035 | self.is_black = True 1036 | else: 1037 | self.is_black = True 1038 | # self has extra blackness 1039 | self.fix_del() 1040 | 1041 | def fix_del(self): 1042 | """ 1043 | Maintains red / black property on node 1044 | deletion. 1045 | """ 1046 | # case 1 1047 | if self.parent is None: 1048 | return 1049 | sib = self.sibling 1050 | # case 2 1051 | if sib.is_red: 1052 | sib.rotate() 1053 | sib = self.sibling 1054 | # case 3 1055 | if (self.parent.is_black and sib.is_black and 1056 | sib.left.is_black and sib.right.is_black): 1057 | sib.is_red = True 1058 | self.parent.fix_del() 1059 | return 1060 | # case 4 1061 | if (self.parent.is_red and sib.is_black and 1062 | sib.left.is_black and sib.right.is_black): 1063 | sib.is_red = True 1064 | self.parent.is_black = True 1065 | return 1066 | # case 5 1067 | if sib.is_black: 1068 | if self is self.parent.left and sib.right.is_black and sib.left.is_red: 1069 | sib.left.rotate() 1070 | elif self is self.parent.right and sib.left.is_black and sib.right.is_red: 1071 | sib.right.rotate() 1072 | sib = self.sibling 1073 | # case 6 1074 | sib.rotate() 1075 | self.parent.sibling.is_black = True 1076 | 1077 | def check(self): 1078 | """ 1079 | Checks whether the subtree rooted at I{self} is a valid 1080 | red black tree U{http://en.wikipedia.org/wiki/Red_black_tree}. 1081 | 1082 | @rtype: C{bool} 1083 | @return: True if the tree is valid, otherwise False 1084 | """ 1085 | leaf_nodes = [] 1086 | for node in self.in_order(): 1087 | if not node.left and not node.right: 1088 | leaf_nodes.append(node) 1089 | if node.is_red: 1090 | if not (node.left.is_black and node.right.is_black): 1091 | return False 1092 | black_height = 0 1093 | if leaf_nodes: 1094 | node = leaf_nodes[0] 1095 | while node.parent: 1096 | if node.is_black: 1097 | black_height += 1 1098 | node = node.parent 1099 | for node in leaf_nodes[1:]: 1100 | height = 0 1101 | while node.parent: 1102 | if node.is_black: 1103 | height += 1 1104 | node = node.parent 1105 | if not height == black_height: 1106 | return False 1107 | return True 1108 | 1109 | def __getitem__(self, key): 1110 | '''Get an item by its key''' 1111 | 1112 | node = self.find(key) 1113 | if node: 1114 | return node.value 1115 | else: 1116 | raise KeyError 1117 | 1118 | def __setitem__(self, key, value): 1119 | '''Add an item using a dictionary-like interface''' 1120 | 1121 | self.add(key, value) 1122 | 1123 | def __delitem__(self, key): 1124 | '''Delete an item using a dictionary-like interface''' 1125 | 1126 | node = self.find(key) 1127 | if node: 1128 | node.del_node() 1129 | else: 1130 | raise KeyError 1131 | 1132 | def iteritems(self): 1133 | '''Iterate over the items in the "dictionary"''' 1134 | 1135 | return ((node.key, node.value) for node in self.in_order()) 1136 | 1137 | def itervalues(self): 1138 | '''Iterate over the values in the "dictionary"''' 1139 | 1140 | return (node.value for node in self.in_order()) 1141 | 1142 | def __len__(self): 1143 | '''Return the number of elements in the container''' 1144 | 1145 | return self.size 1146 | 1147 | -------------------------------------------------------------------------------- /Algorithms/bst/red_black_set_mod.py: -------------------------------------------------------------------------------- 1 | 2 | # pylint: disable=C0302 3 | # C0302: Unfortunately, we want a lot of lines in this module 4 | 5 | # duplicate-code: We m4 preprocess this file into 2 or more versions, so 6 | # of course we have "duplicate" code. But things aren't duplicated in 7 | # the m4. Unfortunately, pylint doesn't allow us to override this 8 | # one, so we use a regex to this-pylint in addition to this disable. 9 | # too-many-lines: We want something self-contained, so this has a lot of 10 | # lines 11 | 12 | '''Red-Black tree and plain Binary Tree module''' 13 | 14 | ##Copyright (c) 2013 duncan g. smith and Dan Stromberg 15 | ## 16 | ##(This is the well-known MIT license) 17 | ## 18 | ##Permission is hereby granted, free of charge, to any person obtaining a 19 | ##copy of this software and associated documentation files (the "Software"), 20 | ##to deal in the Software without restriction, including without limitation 21 | ##the rights to use, copy, modify, merge, publish, distribute, sublicense, 22 | ##and/or sell copies of the Software, and to permit persons to whom the 23 | ##Software is furnished to do so, subject to the following conditions: 24 | ## 25 | ##The above copyright notice and this permission notice shall be included 26 | ##in all copies or substantial portions of the Software. 27 | ## 28 | ##THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 29 | ##OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 30 | ##FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 31 | ##THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 32 | ##OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 33 | ##ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 34 | ##OTHER DEALINGS IN THE SOFTWARE. 35 | 36 | # This code was originally by Duncan G. Smith, but has been modified a 37 | # bit by Dan Stromberg. 38 | 39 | 40 | 41 | from __future__ import division 42 | 43 | import sys 44 | import math 45 | import random 46 | import itertools 47 | 48 | if hasattr(itertools, 'izip_longest'): 49 | MY_ZIP_LONGEST = getattr(itertools, 'izip_longest') 50 | else: 51 | MY_ZIP_LONGEST = getattr(itertools, 'zip_longest') 52 | 53 | try: 54 | next 55 | except NameError: 56 | def next(iterator): 57 | # pylint: disable=redefined-builtin 58 | '''A version of next() for python's that don't have it''' 59 | return iterator.next() 60 | 61 | #class TreeError(Exception): 62 | # '''An exception to raise if the tree gets in a bad state - unused''' 63 | # pass 64 | 65 | def center(string, field_use_width, field_avail_width): 66 | '''Center a string within a given field width''' 67 | field_use = (string + '_' * (field_use_width - 1))[:field_use_width - 1] 68 | pad_width = (field_avail_width - len(field_use)) / 2.0 69 | result = ' ' * int(pad_width) + field_use + ' ' * int(math.ceil(pad_width)) 70 | return result 71 | 72 | class BinaryTree(object): 73 | # pylint: disable=too-many-public-methods,incomplete-protocol 74 | """ 75 | A basic binary tree class. Arbitrary data can be 76 | associated with a tree. A tree that is root has 77 | parent equal to None; a tree that is leaf has left 78 | and right children that are empty trees (sentinels). 79 | An empty tree has left and right children equal to None. 80 | 81 | A tree can have children (or a parent) with equal 82 | data. 83 | """ 84 | 85 | __slots__ = [ 'key', 'left', 'right', 'parent' ] 86 | 87 | def __init__(self, key=None, left=None, right=None, parent=None): 88 | """ 89 | Initialises instance. 90 | 91 | @type key: arbitrary type 92 | @param key: data 93 | # placeholder 94 | # placeholder 95 | @type left: L{BinaryTree} or C{None} 96 | @param left: left child 97 | @type right: L{BinaryTree} or C{None} 98 | @param right: right child 99 | @type parent: L{BinaryTree} or C{None} 100 | @param parent: parent 101 | """ 102 | self.key = key 103 | # placeholder 104 | self.left = left 105 | self.right = right 106 | self.parent = parent 107 | 108 | def __repr__(self): 109 | """ 110 | Returns a string representation of I{self}, containing 111 | the key in I{self} and left and right children. If I{self} 112 | is a sentinel the string "sentinel" is returned. 113 | 114 | @rtype: C{str} 115 | @return: string representation 116 | """ 117 | if self: 118 | return '%s' % (self.key, ) 119 | else: 120 | return "sent" 121 | 122 | def _depth_and_field_width(self): 123 | '''Compute the depth of the tree and the maximum width within the nodes - for pretty printing''' 124 | class maxer(object): 125 | '''Class facilitates computing the max depth of the treap (tree) and max width of the nodes''' 126 | def __init__(self, maximum=-1): 127 | self.depth_max = maximum 128 | self.field_width_max = -1 129 | 130 | def feed(self, node, key, depth, from_left): 131 | '''Check our maximums so far against the current node - updating as needed''' 132 | # pylint: disable=R0913 133 | # R0913: We need a bunch of arguments 134 | dummy = key 135 | # placeholder 136 | dummy = from_left 137 | if depth > self.depth_max: 138 | self.depth_max = depth 139 | repr_node = repr(node) 140 | len_node = len(repr_node) 141 | if len_node > self.field_width_max: 142 | self.field_width_max = len_node 143 | 144 | def result(self): 145 | '''Return the maximums we've computed''' 146 | return (self.depth_max + 1, self.field_width_max) 147 | 148 | max_obj = maxer() 149 | self.detailed_inorder_traversal(max_obj.feed) 150 | return max_obj.result() 151 | 152 | def to_dot(self, initial=True, file_=sys.stdout, visited=None): 153 | """Generate a dot file describing this tree""" 154 | 155 | if visited is None: 156 | visited = set() 157 | if initial: 158 | file_.write('digraph G {\n') 159 | this_node = '%s %s' % (id(self), repr(self)) 160 | if this_node in visited: 161 | return 162 | visited.add(this_node) 163 | if self.left is not None: 164 | file_.write(' "%s" -> "%s %s" [ label="left" ]\n' % (this_node, id(self.left), repr(self.left))) 165 | self.left.to_dot(initial=False, file_=file_, visited=visited) 166 | if self.right is not None: 167 | file_.write(' "%s" -> "%s %s" [ label="right" ]\n' % (this_node, id(self.right), repr(self.right))) 168 | self.right.to_dot(initial=False, file_=file_, visited=visited) 169 | if initial: 170 | file_.write('}\n') 171 | 172 | def __str__(self): 173 | '''Format a tree as a string''' 174 | class Desc(object): 175 | # pylint: disable=R0903 176 | # R0903: We don't need a lot of public methods 177 | '''Build a pretty-print string during a recursive traversal''' 178 | def __init__(self, pretree): 179 | self.tree = pretree 180 | 181 | def update(self, node, key, depth, from_left): 182 | '''Add a node to the tree''' 183 | # pylint: disable=R0913 184 | # R0913: We need a bunch of arguments 185 | dummy = key 186 | # placeholder 187 | self.tree[depth][from_left] = repr(node) 188 | 189 | pretree = [] 190 | depth, field_use_width = self._depth_and_field_width() 191 | field_use_width += 1 192 | for level in range(depth): 193 | string = '_' * (field_use_width - 1) 194 | pretree.append([string] * 2 ** level) 195 | desc = Desc(pretree) 196 | self.detailed_inorder_traversal(desc.update) 197 | result = [] 198 | widest = 2 ** (depth - 1) * field_use_width 199 | for level in range(depth): 200 | two_level = 2 ** level 201 | field_avail_width = widest / two_level 202 | string = ''.join([center(x, field_use_width, field_avail_width) for x in desc.tree[level]]) 203 | # this really isn't useful for more than dozen values or so 204 | result.append(('%2d ' % level) + string) 205 | return '\n'.join(result) 206 | 207 | def __nonzero__(self): 208 | """ 209 | A sentinel node evaluates to False. 210 | 211 | @rtype: C{bool} 212 | @return: True if neither left nor right are None, 213 | otherwise False 214 | """ 215 | return not (self.left is None and self.right is None) 216 | 217 | __bool__ = __nonzero__ 218 | 219 | @property 220 | def is_root(self): 221 | """ 222 | Returns True if I{self} is root. 223 | 224 | @rtype: C{bool} 225 | @return: True if I{self} is root, otherwise False 226 | """ 227 | return self.parent is None 228 | 229 | @property 230 | def sibling(self): 231 | """ 232 | Returns the sibling of I{self}. 233 | 234 | @rtype: L{BinaryTree} or C{None} 235 | @return: sibling 236 | """ 237 | # sibling may be a sentinel node 238 | if self.is_root: 239 | return None 240 | elif self is self.parent.left: 241 | return self.parent.right 242 | else: 243 | return self.parent.left 244 | 245 | @property 246 | def size(self): 247 | """ 248 | Return the number of key items / non-sentinel 249 | nodes in the subtree rooted at I{self}. 250 | 251 | @rtype: C{int} 252 | @return: number of elements 253 | """ 254 | if self: 255 | return self.left.size + self.right.size + 1 256 | return 0 257 | 258 | @property 259 | def num_levels(self): 260 | """ 261 | Returns the number of levels in the subtree 262 | rooted at I{self}. 263 | 264 | @rtype: C{int} 265 | @return: number of levels 266 | """ 267 | if self: 268 | return max(self.left.num_levels, self.right.num_levels) + 1 269 | return 0 270 | 271 | @property 272 | def height(self): 273 | """ 274 | Returns the height of the subtree rooted at 275 | I{self}. This is the number of levels - 1. 276 | 277 | @rtype: C{int} 278 | @return: number of levels 279 | """ 280 | return self.num_levels - 1 281 | 282 | def copy(self, parent=None): 283 | """ 284 | Returns a shallow copy of the subtree rooted 285 | at I{self} (does not copy data). 286 | 287 | @type parent: L{BinaryTree} or C{None} 288 | @param parent: parent 289 | @rtype: L{BinaryTree} 290 | @return: a copy 291 | """ 292 | if self: 293 | copy = self.__class__(key=self.key, parent=parent) 294 | copy.left = self.left.copy(copy) 295 | copy.right = self.right.copy(copy) 296 | else: 297 | copy = self.__class__(parent=parent) 298 | return copy 299 | 300 | # def copy(self, parent=None): 301 | # """ 302 | # Returns a shallow copy of the subtree rooted 303 | # at I{self} (does not copy data). 304 | # 305 | # @type parent: L{BinaryTree} or C{None} 306 | # @param parent: parent 307 | # @rtype: L{BinaryTree} 308 | # @return: a copy 309 | # """ 310 | # if self: 311 | # copy = self.__class__(self.data, parent=parent) 312 | # copy.left = self.left.copy(copy) 313 | # copy.right = self.right.copy(copy) 314 | # else: 315 | # copy = self.__class__(parent=parent) 316 | # return copy 317 | 318 | def del_node(self): 319 | """ 320 | Deletes a node from the subtree rooted at I{self}, 321 | but does not delete the subtree rooted at node. To 322 | delete a node on the basis of its data use the 323 | I{remove} method. 324 | """ 325 | if self.left: 326 | if self.right: 327 | # method chosen randomly in order to 328 | # prevent tree becoming too unbalanced 329 | if random.random() < 0.5: 330 | node = self.left.maximum 331 | self.key = node.key 332 | # placeholder 333 | node.del_node() 334 | else: 335 | node = self.right.minimum 336 | self.key = node.key 337 | # placeholder 338 | node.del_node() 339 | else: 340 | node = self.left 341 | self.key, self.left, self.right = node.key, node.left, node.right 342 | # placeholder 343 | self.left.parent = self 344 | self.right.parent = self 345 | elif self.right: 346 | node = self.right 347 | self.key, self.left, self.right = node.key, node.left, node.right 348 | # placeholder 349 | self.left.parent = self 350 | self.right.parent = self 351 | else: 352 | # make node a sentinel node 353 | self.key = self.left = self.right = None 354 | # placeholder 355 | 356 | def add(self, key): 357 | """ 358 | Adds a node containing I{key} to the subtree 359 | rooted at I{self}, returning the added node. 360 | 361 | @type key: arbitrary type 362 | @param key: key 363 | placeholder 364 | placeholder 365 | @rtype: L{BinaryTree} 366 | @return: (replaced flag, added node) 367 | """ 368 | node, count = self.findNCount(key) 369 | if not node: 370 | node.key = key 371 | # placeholder 372 | node.left, node.right = self.__class__(parent=node), self.__class__(parent=node) 373 | return (False, node, count) 374 | elif node.key == key: 375 | node.key = key 376 | # placeholder 377 | return (True, node, count) 378 | else: 379 | if random.random() < 0.5: 380 | return BinaryTree.add(node.left, key=key) 381 | else: 382 | return BinaryTree.add(node.right, key=key) 383 | 384 | def remove(self, key): 385 | """ 386 | Removes a node containing I{key} from the 387 | subtree rooted at I{self}, raising an error 388 | if the subtree does not contain I{key}. 389 | 390 | @type key: arbitrary type 391 | @param key: data 392 | @raise ValueError: if key is not contained in the 393 | subtree rooted at I{self} 394 | """ 395 | node, count = self.findNCount(key) 396 | if node: 397 | node.del_node() 398 | else: 399 | raise ValueError('tree.remove(x): x not in tree') 400 | return count 401 | 402 | def discard(self, key): 403 | """ 404 | Discards a node containing I{key} from the subtree 405 | rooted at I{self} (without raising an error if the key 406 | is not present). 407 | 408 | @type key: arbitrary type 409 | @param key: data 410 | """ 411 | try: 412 | count = self.remove(key) 413 | except ValueError: 414 | pass 415 | return count 416 | 417 | def count(self, key): 418 | """ 419 | Returns the number of occurrences of I{key} in 420 | the subtree rooted at I{self}. 421 | 422 | @type key: arbitrary type 423 | @param key: data 424 | @rtype: C{int} 425 | @return: data count 426 | """ 427 | if self: 428 | node = self.find(key) 429 | if node: 430 | return node.left.count(key) + node.right.count(key) + 1 431 | return 0 432 | 433 | def __add__(self, iterable): 434 | """ 435 | Returns a tree with elements from the subtree 436 | rooted at I{self} and I{iterable}. I{iterable} 437 | may be any iterable, not necessarily another 438 | tree. 439 | 440 | @type iterable: any iterable type 441 | @param iterable: an iterable object 442 | @rtype: L{BinaryTree} 443 | @return: tree containing the elements in I{self} 444 | and iterable 445 | """ 446 | items = list(self) + list(iterable) 447 | random.shuffle(items) # try to avoid unbalanced tree 448 | tree = self.__class__() 449 | for item in items: 450 | tree.add(item) 451 | return tree 452 | 453 | def __iadd__(self, iterable): 454 | """ 455 | Adds the elements in I{iterable} to the 456 | subtree rooted at I{self}. 457 | 458 | @type iterable: any iterable type 459 | @param iterable: an iterable object 460 | """ 461 | items = list(iterable) 462 | random.shuffle(items) # try to avoid unbalanced tree 463 | for item in items: 464 | self.add(item) 465 | 466 | def find(self, key): 467 | """ 468 | Finds and returns a node containing I{key} in the 469 | subtree rooted at I{self}. If I{key} is not in 470 | the tree, then a sentinel in the location where 471 | I{key} can be inserted is returned. 472 | 473 | @type key: arbitrary type 474 | @param key: data 475 | @rtype: L{BinaryTree} 476 | @return: node containing key, or sentinel 477 | node 478 | """ 479 | if self: 480 | if self.key == key: 481 | return self 482 | elif key < self.key: 483 | return self.left.find(key) 484 | else: 485 | return self.right.find(key) 486 | return self 487 | 488 | def findNCount(self, key, count = 0): 489 | """ 490 | Finds and returns a node containing I{key} in the 491 | subtree rooted at I{self}. If I{key} is not in 492 | the tree, then a sentinel in the location where 493 | I{key} can be inserted is returned. 494 | 495 | @type key: arbitrary type 496 | @param key: data 497 | @rtype: L{BinaryTree} 498 | @return: node containing key, or sentinel 499 | node 500 | """ 501 | if self: 502 | count += 1 503 | if self.key == key: 504 | return self, count +1 505 | elif key < self.key: 506 | return self.left.findNCount(key, count) 507 | else: 508 | return self.right.findNCount(key, count) 509 | return self, count 510 | 511 | @property 512 | def predecessor(self): 513 | """ 514 | Returns the predecessor of I{self}, or None if 515 | self has no predecessor. 516 | 517 | @rtype: L{BinaryTree} or None 518 | @return: predecessor of I{self} 519 | """ 520 | if self.left: 521 | return self.left.maximum 522 | else: 523 | if not self.parent: 524 | return None 525 | if self is self.parent.right: 526 | return self.parent 527 | else: 528 | current, parent = self, self.parent 529 | while parent and parent.left is current: 530 | current, parent = parent, parent.parent 531 | return parent 532 | 533 | @property 534 | def successor(self): 535 | """ 536 | Returns the successor of I{self}, or None if 537 | self has no successor. 538 | 539 | @rtype: L{BinaryTree} or None 540 | @return: successor of I{self} 541 | """ 542 | if self.right: 543 | return self.right.minimum 544 | else: 545 | if not self.parent: 546 | return None 547 | if self is self.parent.left: 548 | return self.parent 549 | else: 550 | current, parent = self, self.parent 551 | while parent and parent.right is current: 552 | current, parent = parent, parent.parent 553 | return parent 554 | 555 | def in_order(self): 556 | """ 557 | Returns a generator of nodes in the subtree 558 | rooted at I{self} in sorted order of node keys. 559 | 560 | @rtype: C{generator} 561 | @return: generator of sorted nodes 562 | """ 563 | if self: 564 | for node in self.left.in_order(): 565 | yield node 566 | yield self 567 | for node in self.right.in_order(): 568 | yield node 569 | 570 | def detailed_inorder_traversal(self, visit, depth=0, from_left=0): 571 | '''Do an inorder traversal - with lots of parameters''' 572 | if self.left: 573 | self.left.detailed_inorder_traversal(visit, depth + 1, from_left * 2) 574 | visit(self, self.key, depth, from_left) 575 | if self.right: 576 | self.right.detailed_inorder_traversal(visit, depth + 1, from_left * 2 + 1) 577 | 578 | def __iter__(self): 579 | """ 580 | Returns a generator of the node keys in 581 | the subtree rooted at I{self} in sorted 582 | order. 583 | 584 | @rtype: C{generator} 585 | @return: generator of sorted node values 586 | """ 587 | return (n.key for n in self.in_order()) 588 | 589 | def in_reverse(self): 590 | """ 591 | Returns a generator of nodes in the subtree 592 | rooted at I{self} in reverse sorted order 593 | of node keys. 594 | 595 | @rtype: C{generator} 596 | @return: generator of reverse sorted nodes 597 | """ 598 | if self: 599 | for node in self.right.in_reverse(): 600 | yield node 601 | yield self 602 | for node in self.left.in_reverse(): 603 | yield node 604 | 605 | def pre_order(self): 606 | """ 607 | Returns a generator of the nodes in the 608 | subtree rooted at I{self} in preorder. 609 | 610 | @rtype: C{generator} 611 | @return: generator of nodes in preorder 612 | """ 613 | if self: 614 | yield self 615 | for node in self.left.pre_order(): 616 | yield node 617 | for node in self.right.pre_order(): 618 | yield node 619 | 620 | def post_order(self): 621 | """ 622 | Returns a generator of the nodes in the 623 | subtree rooted at I{self} in postorder. 624 | 625 | @rtype: C{generator} 626 | @return: generator of nodes in postorder 627 | """ 628 | if self: 629 | for node in self.left.post_order(): 630 | yield node 631 | for node in self.right.post_order(): 632 | yield node 633 | yield self 634 | 635 | @property 636 | def minimum(self): 637 | """ 638 | Returns a node for which the node key is minimum 639 | in the subtree rooted at I{self}. Always returns 640 | the minimum-valued node with no left child. 641 | 642 | @rtype: L{BinaryTree} 643 | @return: node with minimum key value 644 | """ 645 | node = self 646 | while node.left: 647 | node = node.left 648 | return node 649 | 650 | @property 651 | def maximum(self): 652 | """ 653 | Returns a node for which the node key is maximum 654 | in the subtree rooted at I{self}. Always returns 655 | the maximum-valued node with no right child. 656 | 657 | @rtype: L{BinaryTree} 658 | @return: node with maximum key value 659 | """ 660 | node = self 661 | while node.right: 662 | node = node.right 663 | return node 664 | 665 | def is_similar(self, other): 666 | """ 667 | Two binary trees are similar if they have the 668 | same structure. 669 | 670 | @rtype: C{bool} 671 | @return: True if the subtrees rooted at I{self} 672 | and I{other} are similar, otherwise False 673 | """ 674 | if not self and not other: 675 | return True 676 | if self and other: 677 | return self.left.is_similar(other.left) and self.right.is_similar(other.right) 678 | return False 679 | 680 | def is_equivalent(self, other): 681 | """ 682 | Two binary trees are equivalent if they are similar 683 | and corresponding nodes contain the same keys. 684 | 685 | @rtype: C{bool} 686 | @return: True if the subtrees rooted at I{self} 687 | and I{other} are equivalent, otherwise False 688 | """ 689 | if not self and not other: 690 | return True 691 | if self and other: 692 | return (self.key == other.key and 693 | # placeholder 694 | self.left.is_equivalent(other.left) and 695 | self.right.is_equivalent(other.right)) 696 | return False 697 | 698 | # def cmp(self, other): 699 | # """ 700 | # Compares I{self} with I{other} lexocographically. 701 | # 702 | # @type other: L{BinaryTree} 703 | # @param other: tree being compared to I{self} 704 | # @rtype: C{int} 705 | # @return: 0 if the subtrees rooted at I{self} 706 | # and I{other} contain equal values, 707 | # -1 if the ordered values in I{self} are lexicographically 708 | # less than the ordered values in I{other}, and 709 | # 1 if the ordered values in I{self} are lexicographically 710 | # greater than the ordered values in I{other} 711 | # """ 712 | # other_items = iter(other) 713 | # for self_item in self: 714 | # try: 715 | # other_item = next(other_items) 716 | # except StopIteration: 717 | # return 1 718 | # else: 719 | # if self_item < other_item: 720 | # return -1 721 | # elif self_item > other_item: 722 | # return 1 723 | # try: 724 | # next(other_items) 725 | # except StopIteration: 726 | # return 0 727 | 728 | def __cmp__(self, other): 729 | """ 730 | Returns C{0} if the subtrees rooted at 731 | I{self} and I{other} contain equal keys. 732 | I{other} need not be similar to I{self}. 733 | 734 | @type other: L{BinaryTree} 735 | @param other: tree being compared to I{self} 736 | @rtype: C{int} 737 | @return: 0 if the subtrees rooted at I{self} 738 | and I{other} do contain equal values, 739 | -1 if self < other. 740 | 1 if self > other. 741 | """ 742 | # FIXME: This probably should compare values too. 743 | for self_node, other_node in MY_ZIP_LONGEST(self.in_order(), other.in_order()): 744 | if self_node is None: 745 | return -1 746 | elif other_node is None: 747 | return 1 748 | elif self_node.key < other_node.key: 749 | return -1 750 | elif self_node.key > other_node.key: 751 | return 1 752 | return 0 753 | 754 | cmp = __cmp__ 755 | 756 | def __eq__(self, other): 757 | """ 758 | Returns C{True} if the subtrees rooted at 759 | I{self} and I{other} contain equal keys. 760 | I{other} need not be similar to I{self}. 761 | 762 | @type other: L{BinaryTree} 763 | @param other: tree being compared to I{self} 764 | @rtype: C{bool} 765 | @return: True if the subtrees rooted at I{self} 766 | and I{other} contain equal values, otherwise 767 | False 768 | """ 769 | return self.cmp(other) == 0 770 | 771 | def __neq__(self, other): 772 | """ 773 | Returns C{True} if the subtrees rooted at 774 | I{self} and I{other} do not contain equal keys. 775 | I{other} need not be similar to I{self}. 776 | 777 | @type other: L{BinaryTree} 778 | @param other: tree being compared to I{self} 779 | @rtype: C{bool} 780 | @return: True if the subtrees rooted at I{self} 781 | and I{other} do not contain equal values, 782 | otherwise False 783 | """ 784 | return self.cmp(other) != 0 785 | 786 | def __lt__(self, other): 787 | """ 788 | Returns C{True} if the subtrees rooted at 789 | I{self} and I{other} do not contain equal keys. 790 | I{other} need not be similar to I{self}. 791 | 792 | @type other: L{BinaryTree} 793 | @param other: tree being compared to I{self} 794 | @rtype: C{bool} 795 | @return: True if the subtrees rooted at I{self} 796 | and I{other} do not contain equal values, 797 | otherwise False 798 | """ 799 | return self.cmp(other) < 0 800 | 801 | def __gt__(self, other): 802 | return other.cmp(self) < 0 803 | 804 | def __le__(self, other): 805 | return self.cmp(other) <= 0 806 | 807 | def __ge__(self, other): 808 | return self.cmp(other) >= 0 809 | 810 | def __contains__(self, key): 811 | """ 812 | Returns true if I{key} is stored in any node in 813 | the subtree rooted at I{self}. 814 | 815 | @type key: arbitrary type 816 | @param key: data 817 | @rtype: C{bool} 818 | @return: True if I{key} in subtree rooted at 819 | I{self}, otherwise False 820 | """ 821 | return bool(self.find(key)) 822 | 823 | 824 | class RedBlackTree(BinaryTree): 825 | # pylint: disable=attribute-defined-outside-init,maybe-no-member,too-many-public-methods,incomplete-protocol 826 | """ 827 | A binary tree with an extra attribute, is_red. Colour (red 828 | or black) is used to maintain a reasonably balanced tree. 829 | Changes to the tree structure (rotations) are carried out 830 | in such a way that the name bound to the initial empty 831 | tree will always refer to the root node. The data stored 832 | in a given node may change, as well as its connectivity. 833 | e.g. 834 | 835 | >>> import trees 836 | >>> tree = trees.RedBlackTree() 837 | >>> tree.add(2) 838 | 2 False None None 839 | >>> tree.key # tree is bound to root node 840 | 2 841 | >>> tree.add(-1) 842 | -1 True None None 843 | >>> tree.add(1) # tree rotations are performed at this point 844 | 1 False -1 2 845 | >>> tree.key # the root node has changed (i.e. its key has been changed) 846 | 1 847 | >>> 848 | 849 | """ 850 | 851 | __slots__ = BinaryTree.__slots__ + [ 'is_red' ] 852 | 853 | def __init__(self, key=None, left=None, right=None, parent=None): 854 | """ 855 | Initialises instance. 856 | 857 | @type key: arbitrary type 858 | @param key: data 859 | @type left: L{RedBlackTree} or C{None} 860 | @param left: left child 861 | @type right: L{RedBlackTree} or C{None} 862 | @param right: right child 863 | @type parent: L{RedBlackTree} or C{None} 864 | @param parent: parent 865 | """ 866 | super(RedBlackTree, self).__init__(key, left, right, parent) 867 | self.is_red = False # default for sentinel nodes 868 | 869 | @property 870 | def is_black(self): 871 | """ 872 | Returns True if node colour is black. 873 | 874 | @rtype: C{bool} 875 | @return: True if node colour is black, otherwise False 876 | """ 877 | return not self.is_red 878 | 879 | @is_black.setter 880 | def is_black(self, val): 881 | """ 882 | Sets node colour. 883 | """ 884 | self.is_red = not val 885 | 886 | @property 887 | def black_height(self): 888 | """ 889 | @rtype: C{int} 890 | @return: black height of subtree rooted at I{self} 891 | """ 892 | if self.left: 893 | if self.left.is_black: 894 | return self.left.black_height + 1 895 | else: 896 | return self.left.black_height 897 | elif self.right: 898 | if self.right.is_black: 899 | return self.right.black_height + 1 900 | else: 901 | return self.right.black_height 902 | else: 903 | return 0 904 | 905 | def __repr__(self): 906 | """ 907 | Returns a string representation of I{self}, containing 908 | the key in I{self}, the colour, and the key in left and 909 | right children. If I{self} is a sentinel the string 910 | "sentinel" is returned. 911 | 912 | @rtype: C{str} 913 | @return: string representation 914 | """ 915 | if self: 916 | if self.is_red: 917 | color_string = 'red' 918 | else: 919 | color_string = 'blk' 920 | return '%s %s' % (self.key, color_string) 921 | else: 922 | return "sent" 923 | 924 | def copy(self, parent=None): 925 | """ 926 | Returns a shallow copy of the subtree rooted 927 | at I{self} (does not copy key). 928 | 929 | @type parent: L{RedBlackTree} or C{None} 930 | @param parent: parent 931 | @rtype: L{RedBlackTree} 932 | @return: a copy 933 | """ 934 | copy = super(RedBlackTree, self).copy(parent) 935 | copy.is_red = self.is_red 936 | return copy 937 | 938 | def add(self, key): 939 | """ 940 | Adds a node containing I{key} to the subtree 941 | rooted at I{self}, returning the node which 942 | contains I{key}. Note: the returned node might 943 | be an existing node due to key swaps on rotations. 944 | 945 | @type key: arbitrary type 946 | @param key: data 947 | @rtype: L{RedBlackTree} 948 | @return: node containing I{key} 949 | """ 950 | (replaced, node, count) = super(RedBlackTree, self).add(key=key) 951 | if not replaced: 952 | node.is_red = True 953 | node = node.fix_insert() 954 | return node, count 955 | 956 | def fix_insert(self): 957 | """ 958 | Carries out tree rotations, key swaps and recolourings 959 | to ensure that the red / black properties of the tree 960 | are preserved on node insertion. The returned node contains 961 | the same value as I{self} (but might not be the inserted node 962 | due to key swaps / rotations). 963 | 964 | @rtype: L{RedBlackTree} 965 | @return: node containing key originally 966 | contained in I{self} 967 | """ 968 | # case 1 969 | if self.parent is None: 970 | self.is_black = True 971 | return self 972 | # case 2 973 | if self.parent.is_black: 974 | return self 975 | # case 3 976 | sib = self.parent.sibling 977 | if sib.is_red: 978 | self.parent.is_black = True 979 | sib.is_black = True 980 | par = self.parent.parent 981 | par.is_red = True 982 | par.fix_insert() 983 | return self 984 | # cases 4 & 5 985 | par = self.parent.parent 986 | if not (self.parent.left is self) == (par.left is self.parent): 987 | self.rotate() 988 | self.parent.rotate() 989 | return par 990 | else: 991 | self.parent.rotate() 992 | return self 993 | 994 | def rotate(self): 995 | """ 996 | Rotates I{self} with I{self.parent}. Left / right rotations 997 | are performed as dictated by the parent-child relationship. 998 | The colours of I{self} and I{self.parent} are implicity swapped 999 | (as their values are swapped during the rotation). 1000 | """ 1001 | # data swapped and connections updated 1002 | # colours are (implicitly) swapped 1003 | if self is self.parent.left: 1004 | self.key, self.parent.key = self.parent.key, self.key 1005 | # placeholder 1006 | self.parent.left = self.left 1007 | self.parent.left.parent = self.parent 1008 | self.left = self.right 1009 | self.right = self.parent.right 1010 | self.left.parent = self.right.parent = self 1011 | self.parent.right = self 1012 | else: 1013 | self.key, self.parent.key = self.parent.key, self.key 1014 | # placeholder 1015 | self.parent.right = self.right 1016 | self.parent.right.parent = self.parent 1017 | self.right = self.left 1018 | self.left = self.parent.left 1019 | self.left.parent = self.right.parent = self 1020 | self.parent.left = self 1021 | 1022 | def del_node(self): 1023 | """ 1024 | Deletes a node from the subtree rooted at I{self}, 1025 | but does not delete the subtree rooted at node. To 1026 | delete a node on the basis of its key use the 1027 | I{remove} method. 1028 | """ 1029 | # ensure that node being deleted has at most one child 1030 | if self.left: 1031 | if self.right: 1032 | # method chosen randomly in order to 1033 | # prevent tree becoming too unbalanced 1034 | if random.random() < 0.5: 1035 | node = self.left.maximum 1036 | else: 1037 | node = self.right.minimum 1038 | self.key, node.key = node.key, self.key 1039 | # placeholder 1040 | node.del_node() # node has at most one child 1041 | else: 1042 | node = self.left 1043 | self.key, self.left, self.right = node.key, node.left, node.right 1044 | # placeholder 1045 | self.left.parent = self # this might result in sentinel node's parent attribute being set 1046 | self.right.parent = self 1047 | self.is_black = True 1048 | elif self.right: 1049 | node = self.right 1050 | self.key, self.left, self.right = node.key, node.left, node.right 1051 | # placeholder 1052 | self.left.parent = self 1053 | self.right.parent = self 1054 | self.is_black = True 1055 | else: 1056 | # node being deleted is leaf 1057 | self.key = self.left = self.right = None 1058 | # placeholder 1059 | if self.is_red:# or self.sibling: # just make it a sentinel node 1060 | self.is_black = True 1061 | else: 1062 | self.is_black = True 1063 | # self has extra blackness 1064 | self.fix_del() 1065 | 1066 | def fix_del(self): 1067 | """ 1068 | Maintains red / black property on node 1069 | deletion. 1070 | """ 1071 | # case 1 1072 | if self.parent is None: 1073 | return 1074 | sib = self.sibling 1075 | # case 2 1076 | if sib.is_red: 1077 | sib.rotate() 1078 | sib = self.sibling 1079 | # case 3 1080 | if (self.parent.is_black and sib.is_black and 1081 | sib.left.is_black and sib.right.is_black): 1082 | sib.is_red = True 1083 | self.parent.fix_del() 1084 | return 1085 | # case 4 1086 | if (self.parent.is_red and sib.is_black and 1087 | sib.left.is_black and sib.right.is_black): 1088 | sib.is_red = True 1089 | self.parent.is_black = True 1090 | return 1091 | # case 5 1092 | if sib.is_black: 1093 | if self is self.parent.left and sib.right.is_black and sib.left.is_red: 1094 | sib.left.rotate() 1095 | elif self is self.parent.right and sib.left.is_black and sib.right.is_red: 1096 | sib.right.rotate() 1097 | sib = self.sibling 1098 | # case 6 1099 | sib.rotate() 1100 | self.parent.sibling.is_black = True 1101 | 1102 | def check(self): 1103 | """ 1104 | Checks whether the subtree rooted at I{self} is a valid 1105 | red black tree U{http://en.wikipedia.org/wiki/Red_black_tree}. 1106 | 1107 | @rtype: C{bool} 1108 | @return: True if the tree is valid, otherwise False 1109 | """ 1110 | leaf_nodes = [] 1111 | for node in self.in_order(): 1112 | if not node.left and not node.right: 1113 | leaf_nodes.append(node) 1114 | if node.is_red: 1115 | if not (node.left.is_black and node.right.is_black): 1116 | return False 1117 | black_height = 0 1118 | if leaf_nodes: 1119 | node = leaf_nodes[0] 1120 | while node.parent: 1121 | if node.is_black: 1122 | black_height += 1 1123 | node = node.parent 1124 | for node in leaf_nodes[1:]: 1125 | height = 0 1126 | while node.parent: 1127 | if node.is_black: 1128 | height += 1 1129 | node = node.parent 1130 | if not height == black_height: 1131 | return False 1132 | return True 1133 | 1134 | # placeholder 1135 | # placeholder 1136 | 1137 | # placeholder 1138 | # placeholder 1139 | # placeholder 1140 | # placeholder 1141 | # placeholder 1142 | 1143 | # placeholder 1144 | # placeholder 1145 | 1146 | # placeholder 1147 | 1148 | # placeholder 1149 | # placeholder 1150 | 1151 | # placeholder 1152 | # placeholder 1153 | # placeholder 1154 | # placeholder 1155 | # placeholder 1156 | 1157 | # placeholder 1158 | # placeholder 1159 | 1160 | # placeholder 1161 | 1162 | # placeholder 1163 | # placeholder 1164 | 1165 | # placeholder 1166 | 1167 | def __len__(self): 1168 | '''Return the number of elements in the container''' 1169 | 1170 | return self.size 1171 | 1172 | -------------------------------------------------------------------------------- /Algorithms/bst/test-red_black_tree_mod.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | '''Test the red black tree module, plus a little test of the plain binary tree''' 4 | 5 | # import sys 6 | # import pprint 7 | 8 | import red_black_set_mod 9 | import red_black_dict_mod 10 | 11 | def test_tree(): 12 | '''Test the plain binary tree''' 13 | tree = red_black_set_mod.BinaryTree() 14 | items = [6, 3, 9, 14, 7, 5, 13, 2, 1, 10] 15 | for item in items: 16 | tree.add(item) 17 | return tree 18 | 19 | def test_entire_tree_ops(tree, acopy): 20 | '''check copy() and comparisons''' 21 | 22 | all_good = True 23 | 24 | # Interesting: for a while, tree == acopy, but tree also != acopy 25 | if not (tree == acopy): 26 | sys.stderr.write('%s: test_entire_tree_ops: not (tree == acopy) (1)\n' % (sys.argv[0],)) 27 | print(tree) 28 | print(acopy) 29 | all_good = False 30 | 31 | if tree != acopy: 32 | sys.stderr.write('%s: test_entire_tree_ops: tree != acopy (1)\n' % (sys.argv[0],)) 33 | print(tree) 34 | print(acopy) 35 | all_good = False 36 | 37 | if not tree.is_similar(acopy): 38 | sys.stderr.write('%s: test_entire_tree_ops: not tree.is_similar(acopy)\n' % (sys.argv[0],)) 39 | all_good = False 40 | 41 | if not tree.is_equivalent(acopy): 42 | sys.stderr.write('%s: test_entire_tree_ops: not tree.is_equivalent(acopy)\n' % (sys.argv[0],)) 43 | all_good = False 44 | 45 | return all_good 46 | 47 | def test_adding(tree, acopy): 48 | '''check adding and deleting nodes''' 49 | all_good = True 50 | 51 | acopy.add(101) 52 | if tree == acopy: 53 | sys.stderr.write('%s: test_adding: tree == acopy, but should not\n' % (sys.argv[0],)) 54 | all_good = False 55 | 56 | if tree.is_similar(acopy): 57 | sys.stderr.write('%s: test_adding: tree.is_similar(acopy), but should not be\n' % (sys.argv[0],)) 58 | all_good = False 59 | 60 | if tree.is_equivalent(acopy): 61 | sys.stderr.write('%s: test_adding: tree.is_equivalent(acopy), but should not be\n' % (sys.argv[0],)) 62 | all_good = False 63 | 64 | return all_good 65 | 66 | def test_tree_list_and_del_node(tree, acopy): 67 | '''check tree + list and del_node''' 68 | all_good = True 69 | 70 | new_tree = tree + [ 101 ] 71 | if new_tree != acopy: 72 | sys.stderr.write('%s: test_tree_plus_list_and_del_node: new_tree != acopy\n' % (sys.argv[0],)) 73 | print(new_tree) 74 | print(acopy) 75 | all_good = False 76 | 77 | acopy.find(101).del_node() 78 | if tree != acopy: 79 | sys.stderr.write('%s: test_tree_plus_list_and_del_node: tree != acopy (2)\n' % (sys.argv[0],)) 80 | print(new_tree) 81 | print(acopy) 82 | all_good = False 83 | 84 | return all_good 85 | 86 | def test_in(tree): 87 | '''check 'in''' 88 | all_good = True 89 | 90 | if 14 not in tree: 91 | sys.stderr.write('%s: test_in: 14 not in tree\n' % (sys.argv[0],)) 92 | all_good = False 93 | 94 | if 27 in tree: 95 | sys.stderr.write('%s: test_in: 27 in tree\n' % (sys.argv[0],)) 96 | all_good = False 97 | 98 | return all_good 99 | 100 | 101 | def test_min_and_max(tree): 102 | '''check minimum() and maximum()''' 103 | all_good = True 104 | 105 | if tree.minimum.key != 0: 106 | sys.stderr.write('%s: test_min_and_max: min is not -1\n' % (sys.argv[0],)) 107 | all_good = False 108 | 109 | if tree.maximum.key != 17: 110 | sys.stderr.write('%s: test_min_and_max: max is not 17\n' % (sys.argv[0],)) 111 | all_good = False 112 | 113 | return all_good 114 | 115 | def test_contains_and_find(tree): 116 | '''check __contains__() and find()''' 117 | all_good = True 118 | 119 | if tree.find(8).key != 8: 120 | sys.stderr.write('%s: test_contains_and_find: member 8 is not 8\n' % (sys.argv[0],)) 121 | all_good = False 122 | 123 | if 8 not in tree: 124 | sys.stderr.write('%s: test_contains_and_find: 8 not in tree\n' % (sys.argv[0],)) 125 | all_good = False 126 | 127 | if tree.find(-23): 128 | sys.stderr.write('%s: test_contains_and_find: -23 mistakenly found in tree\n' % (sys.argv[0],)) 129 | all_good = False 130 | 131 | if -23 in tree: 132 | sys.stderr.write('%s: test_contains_and_find: -23 mistakenly in tree\n' % (sys.argv[0],)) 133 | all_good = False 134 | 135 | return all_good 136 | 137 | def test_in_order(tree, items): 138 | '''check in_order()''' 139 | all_good = True 140 | 141 | items.sort() 142 | 143 | if items != [node.key for node in tree.in_order()]: 144 | sys.stderr.write('%s: test_in_order: items != in_order traversal\n' % (sys.argv[0],)) 145 | all_good = False 146 | 147 | return all_good 148 | 149 | def test_pred_and_succ(items, tree): 150 | '''check predecessor and successor''' 151 | all_good = True 152 | 153 | items.remove(8) 154 | tree.remove(8) 155 | for i in range(len(items) - 1): 156 | if tree.find(items[i+1]).predecessor.key != items[i]: 157 | sys.stderr.write('%s: predecessor problem\n' % (sys.argv[0],)) 158 | all_good = False 159 | 160 | if tree.find(items[i]).successor.key != items[i+1]: 161 | sys.stderr.write('%s: successor problem\n' % (sys.argv[0],)) 162 | all_good = False 163 | 164 | if tree.find(items[0]).predecessor is not None: 165 | sys.stderr.write('%s: nonexistent predecessor problem\n' % (sys.argv[0],)) 166 | all_good = False 167 | 168 | if tree.find(items[-1]).successor is not None: 169 | sys.stderr.write('%s: nonexistent successor problem\n' % (sys.argv[0],)) 170 | all_good = False 171 | 172 | return all_good 173 | 174 | def test_check(tree): 175 | '''Test tree invariants''' 176 | all_good = True 177 | 178 | if tree.check() != True: 179 | sys.stderr.write('%s: test_check: tree does not check out ok\n' % (sys.argv[0],)) 180 | all_good = False 181 | 182 | return all_good 183 | 184 | def test_uniquing(): 185 | '''Test set with uniquing property''' 186 | all_good = True 187 | 188 | tree = red_black_set_mod.RedBlackTree() 189 | 190 | list_ = list(range(10)) + list(range(-5, 5)) + list(range(5, 15)) 191 | for value in list_: 192 | tree.add(value) 193 | 194 | if tree.check() != True: 195 | sys.stderr.write('%s: test_uniquing: tree does not check out ok\n' % (sys.argv[0],)) 196 | all_good = False 197 | return False 198 | 199 | set_ = set(list_) 200 | list_ = list(set_) 201 | list_.sort() 202 | 203 | list_of_tree = list(tree) 204 | if list_ != list_of_tree: 205 | sys.stderr.write('%s: test_uniquing: unique values are not correct\n' % (sys.argv[0],)) 206 | print(list_) 207 | print(list_of_tree) 208 | all_good = False 209 | 210 | return all_good 211 | 212 | def test_similar(): 213 | '''Test adding to a dictionary''' 214 | 215 | all_good = True 216 | 217 | dict_ = red_black_set_mod.RedBlackTree() 218 | for integer in range(5): 219 | dict_.add(integer) 220 | 221 | return all_good 222 | 223 | 224 | def test_dict_add(): 225 | '''Test adding to a dictionary''' 226 | 227 | all_good = True 228 | 229 | dict_ = red_black_dict_mod.RedBlackTree() 230 | for integer in range(5): 231 | dict_.add(key=integer, value=2**integer) 232 | 233 | return all_good 234 | 235 | def test_dict_find(): 236 | '''Test adding to a dictionary''' 237 | 238 | all_good = True 239 | 240 | dict_ = red_black_dict_mod.RedBlackTree() 241 | for integer in range(5): 242 | dict_.add(key=integer, value=2**integer) 243 | 244 | found_key = dict_.find(3).key 245 | 246 | if found_key != 3: 247 | sys.stderr.write('%s: test_dict_find: 3th element is not 3, instead got %s\n' % (sys.argv[0], found_key)) 248 | all_good = False 249 | 250 | return all_good 251 | 252 | 253 | def test_dict_setitem(): 254 | '''Test dictionary-like operation: __setitem__''' 255 | 256 | all_good = True 257 | 258 | dict_ = red_black_dict_mod.RedBlackTree() 259 | 260 | for number in range(10): 261 | dict_[number] = 2**number 262 | 263 | if len(dict_) != 10: 264 | sys.stderr.write('%s: test_dict_setitem: len(dict_) is not 10\n' % (sys.argv[0], )) 265 | all_good = False 266 | 267 | return all_good 268 | 269 | 270 | def test_dict_getitem(): 271 | '''Test dictionary-like operation: __getitem__''' 272 | 273 | all_good = True 274 | 275 | dict_ = red_black_dict_mod.RedBlackTree() 276 | 277 | for number in range(10): 278 | dict_[number] = 2**number 279 | 280 | value = dict_[0] 281 | if value != 1: 282 | sys.stderr.write('%s: test_dict_getitem: 0th element of dict_ is not 1, instead got %s\n' % (sys.argv[0], value)) 283 | all_good = False 284 | 285 | value = dict_[2] 286 | if value != 4: 287 | sys.stderr.write('%s: test_dict_getitem: 2nd element of dict_ is not 4, instead got %s\n' % (sys.argv[0], value)) 288 | all_good = False 289 | 290 | value = dict_[9] 291 | if value != 512: 292 | sys.stderr.write('%s: test_dict_getitem: 9th element of dict_ is not 512, instead got %s\n' % (sys.argv[0], value)) 293 | all_good = False 294 | 295 | return all_good 296 | 297 | def test_dict_delitem(): 298 | '''Test dictionary-like operation: __delitem__''' 299 | 300 | all_good = True 301 | 302 | dict_ = red_black_dict_mod.RedBlackTree() 303 | 304 | for number in range(10): 305 | dict_[number] = 2**number 306 | 307 | for number in range(0, 10, 2): 308 | del dict_[number] 309 | 310 | if len(dict_) != 5: 311 | sys.stderr.write('%s: test_dict_delitem: len(dict_) is not 5\n' % (sys.argv[0], )) 312 | all_good = False 313 | 314 | if dict_[1] != 2: 315 | sys.stderr.write('%s: test_dict_delitem: 1th element of dict_ is not 2\n' % (sys.argv[0], )) 316 | all_good = False 317 | 318 | if dict_[9] != 512: 319 | sys.stderr.write('%s: test_dict_delitem: 9th element of dict_ is not 512\n' % (sys.argv[0], )) 320 | all_good = False 321 | 322 | return all_good 323 | 324 | def test_dict_iteritems(): 325 | '''Test dictionary-like operation: __iteritems__''' 326 | 327 | all_good = True 328 | 329 | dict_ = red_black_dict_mod.RedBlackTree() 330 | list_ = [] 331 | 332 | for number in range(10): 333 | dict_[number] = 2**number 334 | list_.append((number, 2**number)) 335 | 336 | if list(dict_.iteritems()) != list_: 337 | sys.stderr.write('%s: test_dict_iteritems: dict_.iteritems() != list_\n' % (sys.argv[0], )) 338 | all_good = False 339 | 340 | return all_good 341 | 342 | def test_dict_itervalues(): 343 | '''Test dictionary-like operation: __itervalues__''' 344 | 345 | all_good = True 346 | 347 | dict_ = red_black_dict_mod.RedBlackTree() 348 | list_ = [] 349 | 350 | for number in range(10): 351 | dict_[number] = 2**number 352 | list_.append(2**number) 353 | 354 | if list(dict_.itervalues()) != list_: 355 | sys.stderr.write('%s: test_dict_itervalues: dict_.itervalues() != list_\n' % (sys.argv[0], )) 356 | all_good = False 357 | 358 | return all_good 359 | 360 | def test_successor(): 361 | '''Test the successor property''' 362 | 363 | all_good = True 364 | 365 | # create a "dictionary" without duplicates 366 | dict_ = red_black_dict_mod.RedBlackTree() 367 | expected_keys = [] 368 | expected_values = [] 369 | for i in range(10): 370 | dict_[i] = 2 ** i 371 | expected_keys.append(i) 372 | expected_values.append(2 ** i) 373 | # for i in range(10): 374 | # dict_[i] = 2 ** i 375 | 376 | expected_keys.sort() 377 | expected_values.sort() 378 | 379 | keys_list = [] 380 | values_list = [] 381 | 382 | node = dict_.find(0) 383 | while node is not None: 384 | keys_list.append(node.key) 385 | values_list.append(node.value) 386 | node = node.successor 387 | 388 | if keys_list != expected_keys: 389 | sys.stderr.write('%s: test_successor: keys_list != expected_keys\n' % (sys.argv[0], )) 390 | sys.stderr.write('keys_list:\n%s\n' % (pprint.pformat(keys_list))) 391 | sys.stderr.write('exected_keys:\n%s\n' % (pprint.pformat(expected_keys))) 392 | all_good = False 393 | 394 | if values_list != expected_values: 395 | sys.stderr.write('%s: test_successor: values_list != expected_values\n' % (sys.argv[0], )) 396 | sys.stderr.write('values_list:\n%s\n' % (pprint.pformat(values_list))) 397 | sys.stderr.write('exected_values:\n%s\n' % (pprint.pformat(expected_values))) 398 | all_good = False 399 | 400 | return all_good 401 | 402 | 403 | def test(): 404 | '''Run the red black tree tests''' 405 | all_good = True 406 | 407 | rbset = red_black_set_mod.RedBlackTree() 408 | rbset_items = [6, 15, 16, 17, 5, 8, 0, 4, 10] 409 | for item in rbset_items: 410 | rbset.add(item) 411 | acopy = rbset.copy() 412 | 413 | 414 | dict_items = list(range(10)) 415 | dict_ = red_black_dict_mod.RedBlackTree() 416 | for element in dict_items: 417 | dict_[element] = 2 ** element 418 | 419 | all_good &= test_entire_tree_ops(rbset, acopy) 420 | all_good &= test_adding(rbset, acopy) 421 | all_good &= test_tree_list_and_del_node(rbset, acopy) 422 | all_good &= test_min_and_max(rbset) 423 | all_good &= test_contains_and_find(rbset) 424 | all_good &= test_in_order(rbset, rbset_items) 425 | all_good &= test_pred_and_succ(dict_items, dict_) 426 | all_good &= test_check(rbset) 427 | all_good &= test_uniquing() 428 | all_good &= test_similar() 429 | all_good &= test_dict_add() 430 | all_good &= test_dict_find() 431 | all_good &= test_dict_setitem() 432 | all_good &= test_dict_getitem() 433 | all_good &= test_dict_delitem() 434 | all_good &= test_dict_iteritems() 435 | all_good &= test_dict_itervalues() 436 | all_good &= test_successor() 437 | 438 | if not all_good: 439 | sys.stderr.write('%s: One or more tests failed\n' % (sys.argv[0],)) 440 | sys.exit(1) 441 | 442 | test_tree() 443 | test() 444 | -------------------------------------------------------------------------------- /Algorithms/bubble_insert_merge_sort.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Created on Aug 12, 2014 3 | 4 | @author: Gary 5 | ''' 6 | 7 | import time 8 | 9 | def sort_me(arr): 10 | sort_me_merge(arr,0,len(arr)-1) 11 | 12 | def sort_me_merge(arr,p,r): 13 | # This is a merge sort 14 | if ( p < r ): 15 | q = (p + r) / 2 16 | sort_me_merge( arr,p ,q) 17 | sort_me_merge( arr,q+1,r) 18 | 19 | #merge 20 | p2 = p 21 | q2 = q+1 22 | arrTemp = list(arr[p:r+1]) 23 | i = 0 24 | while ( p2 <= q and q2 <= r ): 25 | if ( arr[p2] > arr[q2] ): # then swap 26 | arrTemp[i] = arr[q2] 27 | q2 = q2 + 1 28 | else: 29 | arrTemp[i] = arr[p2] 30 | p2 = p2 + 1 31 | i = i + 1 32 | if ( p2 <= q ): 33 | arrTemp[r-p-q+p2:r-p+1] = arr[p2:q+1] 34 | arr[p:r+1] = list(arrTemp) 35 | 36 | def sort_me_insertion(arr): 37 | # This is an insertion sort 38 | for j in range(1,len(arr)): 39 | key = arr[j] 40 | i = j - 1 41 | while ( i>=0 and arr[i]>key ): 42 | arr[i+1] = arr[i] 43 | i = i-1 44 | arr[i+1] = key 45 | 46 | def sort_me_bubble(arr): 47 | # This is a bubble sort 48 | for i in range(0,len(arr)-1): 49 | for j in range(i+1,len(arr)): 50 | if arr[i] > arr[j]: # if TRUE then swap i and j in array 51 | temp = arr[i] 52 | arr[i] = arr[j] 53 | arr[j] = temp 54 | 55 | def time_me(method): 56 | def wrapper(*args, **kw): 57 | startTime = int(round(time.time() * 1000)) 58 | result = method(*args, **kw) 59 | endTime = int(round(time.time() * 1000)) 60 | 61 | #print(endTime - startTime,'ms') 62 | return [result,endTime-startTime] 63 | 64 | return wrapper 65 | 66 | @time_me 67 | def sort_me_timed(arr): 68 | sort_me(arr) 69 | 70 | def is_sorted(lst, key=lambda x, y: x < y): 71 | for i, el in enumerate(lst[1:]): 72 | if key(el, lst[i]): 73 | return False 74 | return True 75 | 76 | def generate_shuffled_array(nElements,isPrint=False): 77 | import numpy as np 78 | arr = np.arange(nElements) 79 | if isPrint: print(arr) 80 | np.random.shuffle(arr) 81 | if isPrint: print(arr) 82 | return arr 83 | 84 | nElements = 100 85 | for i in range(0,6): 86 | arr = generate_shuffled_array(nElements,False) 87 | timed_result = sort_me_timed(arr) 88 | print(nElements,timed_result[1]) 89 | if not is_sorted(arr): 90 | print("Array not sorted") 91 | if nElements < 25: print(arr) 92 | nElements *= 2; -------------------------------------------------------------------------------- /Algorithms/dijkstraWorkshop/dijkstraWorkshop.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calvint/AlgorithmsOneProblems/945ea58295d277e484dfd4f5cc2cac033ccd2b2b/Algorithms/dijkstraWorkshop/dijkstraWorkshop.zip -------------------------------------------------------------------------------- /Algorithms/dijkstraWorkshop/graph.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Created on Nov 8, 2014 3 | 4 | @author: Gary 5 | ''' 6 | from numpy.f2py.auxfuncs import throw_error 7 | 8 | class graph: 9 | def __init__(self): 10 | self.vertexNameArray = [] # vertex names in an array 11 | self.vertexIndexMap = {} # vertex names to index dictionary 12 | self.vertexPositionArray = [] # x,y pair position array 13 | self.edgeArray = [] # array of (vertex index pair, weight) 14 | 15 | def addVertex(self, name, x, y): 16 | self.vertexIndexMap[name] = self.vCount() 17 | self.vertexNameArray.append(name) 18 | self.vertexPositionArray.append((x,y)) 19 | 20 | def addEdge(self, vName1, vName2, weight): 21 | self.edgeArray.append((self.vertexIndexMap[vName1],self.vertexIndexMap[vName2],weight)) 22 | 23 | def vCount(self): return len(self.vertexNameArray) 24 | 25 | def eCount(self): return len(self.edgeArray) 26 | 27 | # Access functions for vertex information 28 | def vX( self, i): return self.vertexPositionArray[i][0] 29 | def vY( self, i): return self.vertexPositionArray[i][1] 30 | def vName(self, i): return self.vertexNameArray[i] 31 | 32 | # Access functions for edge information 33 | def eV0X( self, i): return self.vertexPositionArray[self.edgeArray[i][0]][0] 34 | def eV0Y( self, i): return self.vertexPositionArray[self.edgeArray[i][0]][1] 35 | def eV1X( self, i): return self.vertexPositionArray[self.edgeArray[i][1]][0] 36 | def eV1Y( self, i): return self.vertexPositionArray[self.edgeArray[i][1]][1] 37 | def eWght(self, i): return self.edgeArray[i][2] 38 | 39 | def weight_between(self, x, y ): 40 | #weight going from x to y 41 | for edge in self.edgeArray: 42 | if self.vertexNameArray[edge[0]] == x and self.vertexNameArray[edge[1]] == y: 43 | return edge[2] 44 | return None 45 | 46 | def relax(self, u, v,source): 47 | if self.weight_between(u,v) == None: 48 | raise Exception("finding weight between two nodes that are not connected") 49 | if self.find_distance(v, source) > self.find_distance(u, source) + self.weight_between(u, v): 50 | self.set_vertex(v, self.find_distance(u, source) + self.weight_between(u, v), source) 51 | self.set_previous(v, u, source) 52 | 53 | def verticies_unseen(self, source): 54 | #checks if there are any remaining verticies in Q 55 | for vertex in source: 56 | distance, previous_vertex, in_s = source[vertex] 57 | if in_s == False: 58 | return True 59 | return False 60 | 61 | def extract_min(self, source): 62 | #well I didn't use a heap so this is what I'm left with... 63 | result = [] 64 | for vertex in source: 65 | distance, previous_vertex, in_s = source[vertex] 66 | if in_s == False: 67 | if result == [] or result[1] > distance: 68 | result = [vertex, distance, previous_vertex] 69 | #remove resulting vertex from Q 70 | source[result[0]][2] = True 71 | return result 72 | 73 | def find_adjacent(self, u): 74 | result = [] 75 | for edge in self.edgeArray: 76 | if edge[0] == self.vertexIndexMap[u]: 77 | result.append(self.vertexNameArray[edge[1]]) 78 | return result 79 | 80 | def find_distance(self, vertex, source): 81 | return source[vertex][0] 82 | 83 | def set_vertex(self, vertex, distance, source): 84 | source[vertex][0] = distance 85 | 86 | def set_previous(self, vertex, previous, source): 87 | source[vertex][1] = previous 88 | 89 | def dijkstra(self, s): 90 | initializedSingleSource = {} 91 | for vertex in self.vertexIndexMap.keys(): 92 | initializedSingleSource[vertex] = [float("inf"), None, False] 93 | initializedSingleSource[s][0] = 0 94 | S = {} 95 | while self.verticies_unseen(initializedSingleSource): 96 | u, vertex_distance, previous_vertex = self.extract_min(initializedSingleSource) 97 | S[u] = [vertex_distance, previous_vertex] 98 | for v in self.find_adjacent(u): 99 | self.relax(u, v, initializedSingleSource) 100 | return S 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /Algorithms/dijkstraWorkshop/graph.txt: -------------------------------------------------------------------------------- 1 | a 20 30 2 | b 200 40 3 | c 30 350 4 | d 350 380 5 | e 100 300 6 | ---------- 7 | a b 3 8 | b c 4 9 | a c 5 10 | a d 4 11 | b d 2 12 | c d 1 13 | e a 2 14 | a e 5 15 | e c 3 16 | e d 3 17 | d e 4 18 | -------------------------------------------------------------------------------- /Algorithms/dijkstraWorkshop/graphParseAndPlot.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Created on Nov 8, 2014 3 | 4 | @author: Gary 5 | ''' 6 | 7 | import fileinput 8 | import graph 9 | import matplotlib.pyplot as plt 10 | from matplotlib.path import Path 11 | import matplotlib.patches as patches 12 | import math 13 | 14 | def plotEdge( plot_axis, x0, y0, x1, y1, weight, clr): 15 | d0 = 4 # This is an offset so the edge is not drawn to the middle of vertex 16 | d2 = 20 # This is an offset to the end of the arrow tails 17 | dx = x1-x0 18 | dy = y1-y0 19 | length = math.sqrt(dx*dx+dy*dy) 20 | if length > 0: 21 | vx = dx/length 22 | vy = dy/length 23 | #plot_axis.plot([x0+vx*d0,x1-vx*d0],[y0+vy*d0,y1-vy*d0], color=clr) # Draw a line 24 | #plot_axis.text( x0+dx/2, y0+dy/2, weight) 25 | 26 | verts = [(x0+vy*d0,y0-vx*d0),(x0+vy*40,y0-vx*40),(x1-vx*80,y1-vy*80),(x1-vx*d0,y1-vy*d0),] 27 | codes = [Path.MOVETO,Path.CURVE4,Path.CURVE4,Path.CURVE4,] 28 | path = Path(verts,codes) 29 | patch = patches.PathPatch( path, facecolor = 'none', edgecolor = clr) 30 | plot_axis.add_patch( patch ) 31 | 32 | plot_axis.plot([x1-vx*d2+vy*3,x1-vx*d0],[y1-vy*d2-vx*3,y1-vy*d0], color=clr) 33 | plot_axis.plot([x1-vx*d2-vy*3,x1-vx*d0],[y1-vy*d2+vx*3,y1-vy*d0], color=clr) 34 | 35 | plot_axis.text( x0+dx/2+vy*10, y0+dy/2-vx*10, weight) 36 | 37 | # Parse graph.txt and populate mygraph structure. 38 | mygraph = graph.graph() 39 | isVertices = True 40 | first_vertex = "" 41 | for line in fileinput.input("graph.txt"): 42 | if isVertices: 43 | if "----------" in line: 44 | isVertices = False 45 | else: #read vertices in this part 46 | split = line.split() 47 | mygraph.addVertex(split[0],float(split[1]),float(split[2])) 48 | if first_vertex == "": 49 | first_vertex = split[0] 50 | else: #read edges in this part 51 | split = line.split() 52 | mygraph.addEdge(split[0], split[1], float(split[2])) 53 | print line, isVertices 54 | 55 | #find total shortest path weights 56 | weights = mygraph.dijkstra(first_vertex) 57 | 58 | fig = plt.figure() 59 | plt_ax = fig.add_subplot(111) 60 | 61 | # Display vertices 62 | minX = minY = 1e1000 63 | maxX = maxY = -1e1000 64 | for iV in range (0, mygraph.vCount()): 65 | x = mygraph.vX(iV) 66 | y = mygraph.vY(iV) 67 | plt_ax.plot(x,y,'wo', ms = 15) 68 | minX = min(minX,x) 69 | minY = min(minY,y) 70 | maxX = max(maxX,x) 71 | maxY = max(maxY,y) 72 | vertex_name = mygraph.vName(iV) 73 | vertex_distance, previous_vertex = weights[vertex_name] 74 | plt_ax.text(x, y, mygraph.vName(iV) + "(" + str(vertex_distance) + ", " + str(previous_vertex) + ")" , ha = 'center', va = 'center') 75 | # plt_ax.text(x, y, "z" , ha = 'center', va = 'center') 76 | padX = .10*(maxX-minX)+10 77 | padY = .10*(maxY-minY)+10 78 | plt_ax.axis([minX-padX, maxX+padX, minY-padY, maxY+padY]) 79 | 80 | # Display edges 81 | for iE in range (0, mygraph.eCount()): 82 | x0 = mygraph.eV0X(iE) 83 | y0 = mygraph.eV0Y(iE) 84 | x1 = mygraph.eV1X(iE) 85 | y1 = mygraph.eV1Y(iE) 86 | plotEdge(plt_ax, x0, y0, x1, y1, mygraph.eWght(iE), '0.9') 87 | 88 | plt.show() 89 | -------------------------------------------------------------------------------- /Algorithms/geany_run_script.bat: -------------------------------------------------------------------------------- 1 | python "workshop2.py" 2 | 3 | pause 4 | del "%0" 5 | 6 | pause 7 | -------------------------------------------------------------------------------- /Algorithms/huffmanWorkshop.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calvint/AlgorithmsOneProblems/945ea58295d277e484dfd4f5cc2cac033ccd2b2b/Algorithms/huffmanWorkshop.zip -------------------------------------------------------------------------------- /Algorithms/huffmanWorkshop/huffman.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Created on Oct 26, 2014 3 | 4 | @author: Gary 5 | ''' 6 | 7 | import huffman_tree 8 | import os 9 | from collections import Counter 10 | from bitarray import bitarray 11 | from Tkconstants import FIRST 12 | # I used the module bitarray to write the binary files, 13 | # read the binary files, and decode the binary to an original 14 | # message. 15 | # 16 | # bitarray is easy to install with the python package manager (pip), 17 | # but on my computer I was required to install a python c++ compiler 18 | # as a dependency. To install pip, find the file get-pip.py on the 19 | # internet and run it in a command prompt. 20 | # To install bit array use the following command: 21 | # python -m pip install bitarray 22 | # 23 | # Any dependency issues will be brought up on the command line when 24 | # that command is entered. 25 | 26 | 27 | 28 | #f_out = open('outfile.txt', 'w') 29 | 30 | ''' Count up the occurrences of each character ''' 31 | 32 | ''' 33 | USE "Counter()" container that you imported!!!! 34 | cnt = Here it is a counter of characters 35 | ''' 36 | cnt = Counter() 37 | 38 | with open('infile2.txt' , 'r') as f: 39 | while True: 40 | c = f.read(1) 41 | if not c: 42 | print "End of file" 43 | break 44 | #INSERT use of Counter() here!!!! 45 | cnt[c] += 1 46 | # print "Read a character:", c 47 | f.close() 48 | 49 | ''' Create a Huffman Tree ''' 50 | 51 | ''' 52 | USE a different instance of "Counter()" container!!!! 53 | huffmanSubtrees = This time it will be a counter of huffmanNode type 54 | ''' 55 | huffmanSubtrees = Counter() 56 | 57 | for charCnt in cnt.most_common(): 58 | # Here we need code to copy from charCnt to huffmanSubtrees!!!! 59 | # We are creating new HuffmanNode and stuffing it into huffmanSubtree container 60 | # (charCnt[0],charCnt[1]) is a tupple with (character, countOfCharacter) 61 | node = huffman_tree.Node(charCnt[0], charCnt[1]) 62 | huffmanSubtrees[node] = charCnt[1] 63 | 64 | 65 | 66 | 67 | 68 | 69 | while len(huffmanSubtrees) > 1 : 70 | # Take the two least common Nodes 71 | node1 = huffmanSubtrees.most_common()[len(huffmanSubtrees)-1][0] 72 | node2 = huffmanSubtrees.most_common()[len(huffmanSubtrees)-2][0] 73 | # Create a new Node with the two above as a leaf (TODO) and add to huffmanSubtrees (TODO) 74 | newNode, nodeCount = node2.join(node1) 75 | huffmanSubtrees[newNode] = nodeCount 76 | 77 | # Delete the old nodes 78 | del huffmanSubtrees[node1] 79 | del huffmanSubtrees[node2] 80 | 81 | ''' Print our Huffman Tree ''' 82 | new_root = huffmanSubtrees.most_common()[0][0] 83 | new_root.print_tree_nice(0,0) 84 | 85 | 86 | #make huffman keys 87 | huffman_encoding = new_root.huffman_map() 88 | 89 | 90 | #make a bitarray from infile2.txt using keys 91 | bitArray = bitarray() 92 | with open('infile2.txt' , 'r') as f: 93 | while True: 94 | c = f.read(1) 95 | if not c: 96 | print "End of file" 97 | break 98 | binChar = bitarray(huffman_encoding[c]) 99 | bitArray += binChar 100 | 101 | #write bitArray to file 102 | with open(("outfile.bin"), "wb") as fh: 103 | bitArray.tofile(fh) 104 | fh.close() 105 | 106 | #read bits from file 107 | bitArrayTwo = bitarray() 108 | with open(("outfile.bin"), "rb") as fhTwo: 109 | bitArrayTwo.fromfile(fhTwo) 110 | 111 | #interpret message 112 | 113 | #the cheating way.. 114 | for char in huffman_encoding.keys(): 115 | huffman_encoding[char] = bitarray(huffman_encoding[char]) 116 | print "".join(bitArrayTwo.decode(huffman_encoding)) 117 | 118 | print "original file size: " + str(os.path.getsize("infile2.txt")) + "\n" 119 | print "compressed file size: " + str(os.path.getsize("outfile.bin")) + "\n" 120 | 121 | """If I was going to do this without bitarray I would first 122 | read the bits of the file into a buffer variable. I would then 123 | loop through the bits and append them to a string variable. At 124 | each itteration of the loop I would check to see if the string 125 | variable contained a sequence that was in my huffman dictionary. 126 | If so, I would add the corrisponding charicter to the result variable, 127 | and then clear the string variable. eventually you would have 128 | the whole file written out into the result variable. 129 | """ 130 | 131 | 132 | 133 | 134 | 135 | -------------------------------------------------------------------------------- /Algorithms/huffmanWorkshop/huffmanWorkshop.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calvint/AlgorithmsOneProblems/945ea58295d277e484dfd4f5cc2cac033ccd2b2b/Algorithms/huffmanWorkshop/huffmanWorkshop.zip -------------------------------------------------------------------------------- /Algorithms/huffmanWorkshop/huffman_tree.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | 3 | class Node: 4 | """ 5 | Tree node: left and right child + data which can be any object 6 | """ 7 | def __init__(self, char, count): 8 | """ 9 | Node constructor 10 | 11 | @param data node data object 12 | """ 13 | self.left = None 14 | self.right = None 15 | self.parnt = None 16 | self.char = char 17 | self.count = count 18 | 19 | def join(self, second_node): 20 | """ 21 | Join two nodes into a single subtree 22 | 23 | @param second node to join 24 | @returns the root of combined subtree 25 | """ 26 | retVal = Node('',0) 27 | 28 | #insert good code here!!!! 29 | retVal.right = self 30 | retVal.left = second_node 31 | retVal.count = self.count + second_node.count 32 | self.parnt = retVal 33 | second_node.parnt = retVal 34 | return retVal, retVal.count 35 | 36 | 37 | def print_tree_nice(self, space_cnt, depth): 38 | """ 39 | Print tree content in tree nicely 40 | Use .(0,0) as default call 41 | """ 42 | if self.right: 43 | self.right.print_tree_nice(space_cnt+4, depth+1) 44 | 45 | if self.char == "": 46 | print (" "*space_cnt, depth, "<", sep='' ) 47 | elif self.char == ' ': 48 | print (" "*space_cnt, "_space", "(", self.count, ")") 49 | elif self.char == '\n': 50 | print (" "*space_cnt, "\\n", "(", self.count, ")") 51 | elif self.char == '\r': 52 | print (" "*space_cnt, "\\r", "(", self.count, ")") 53 | else: 54 | print (" "*space_cnt, self.char, "(", self.count, ")") 55 | 56 | if self.left: 57 | self.left.print_tree_nice(space_cnt+4, depth+1) 58 | 59 | def huffman_map(self,string = "", dict = {}): 60 | if self.left != None: 61 | if self.left.char == "": 62 | self.left.huffman_map(string + "0", dict) 63 | if self.left.char != "": 64 | dict[self.left.char] = string + "0" 65 | if self.right != None: 66 | if self.right.char == "": 67 | self.right.huffman_map(string + "1", dict) 68 | if self.right.char != "": 69 | dict[self.right.char] = string + "1" 70 | return dict 71 | 72 | 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /Algorithms/huffmanWorkshop/huffman_tree.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calvint/AlgorithmsOneProblems/945ea58295d277e484dfd4f5cc2cac033ccd2b2b/Algorithms/huffmanWorkshop/huffman_tree.pyc -------------------------------------------------------------------------------- /Algorithms/huffmanWorkshop/outfile.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calvint/AlgorithmsOneProblems/945ea58295d277e484dfd4f5cc2cac033ccd2b2b/Algorithms/huffmanWorkshop/outfile.bin -------------------------------------------------------------------------------- /Algorithms/huffmanWorkshop/test.py: -------------------------------------------------------------------------------- 1 | from bitarray import bitarray 2 | 3 | a = bitarray("01001001001011100110101") 4 | b = bitarray("1011010100100101") 5 | print b 6 | print a 7 | a += b 8 | print a 9 | -------------------------------------------------------------------------------- /Algorithms/workshop2.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | """Heapsort Workshop 4 | Calvin Troutt 9/7/14 5 | """ 6 | def makeHeap(array): 7 | result = {} 8 | index = 0 9 | for number in array: 10 | index += 1 11 | result[index] = number 12 | return result 13 | 14 | def maxHeapify(heap, index): 15 | left = index << 1 16 | right = (index << 1) +1 17 | if left <= len(heap) and heap[left] > heap[index]: 18 | largest = left 19 | else: 20 | largest = index 21 | if right <= len(heap) and heap[right] > heap[largest]: 22 | largest = right 23 | if largest != index: 24 | tempVar = heap[index] 25 | heap[index] = heap[largest] 26 | heap[largest] = tempVar 27 | maxHeapify(heap, largest) 28 | 29 | def buildMaxHeap(heap): 30 | for i in range(len(heap)/2, 0, -1): 31 | maxHeapify(heap, i) 32 | 33 | def heapSort(heap): 34 | buildMaxHeap(heap) 35 | result = [] 36 | for i in range(len(heap), 1, -1): 37 | result.append(heap[1]) 38 | heap[1] = heap[i] 39 | del heap[i] 40 | maxHeapify(heap, 1) 41 | result.append(heap[1]) 42 | return result 43 | 44 | heap = makeHeap([3,14,5,76,24,2]) 45 | result = heapSort(heap) 46 | for i in result: 47 | print(i) 48 | 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AlgorithmsOneProblems 2 | Homeworks from Algorithms 1 3 | 4 | We were implementing examples from this textbook: 5 | Thomas H. Cormen, Charles E. Leiserson, Ronald L. Rivest, 6 | Clifford Stein Introduction to Algorithms, Third Edition 2009 7 | --------------------------------------------------------------------------------