├── .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 |
--------------------------------------------------------------------------------