├── .gitignore ├── LICENSE ├── README.md ├── dp ├── bellman_ford.py ├── coinchange.py ├── dijkstra.py ├── floyd.py ├── johnsons_apsp.py ├── kadane.py ├── kp.py ├── kpdata.txt ├── lcs.py ├── longest_subsequence.py └── max_subsquare_matrix.py ├── graphs ├── __init__.py ├── clustering.py ├── digraph.py ├── eulerian_tour.py ├── graph.py └── graph_algorithms.py ├── heaps ├── __init__.py ├── heapsort.py ├── maxheap.py └── minheap.py ├── lists ├── __init__.py ├── queue.py ├── singlylinkedlist.py └── stack-adt.py ├── misc ├── GCD.py ├── __init__.py ├── max_area_histogram.py ├── modular_exponentiation.py ├── modular_multiplicative_inverse.py ├── rabin_miller_primality_test.py ├── shuffle.py └── sieve_of_eratosthenes.py ├── sorting and basics ├── binary_search.py ├── countinversion.py ├── karatsuba.py ├── quicksort.py ├── scheduling.py ├── selection_deter.py ├── selection_random.py └── sorting.py ├── tests ├── __init__.py ├── assign.py ├── digraph_test.py ├── gcd_test.py ├── graph_algorithms_test.py ├── graph_test.py ├── heap_test.py ├── lcs_test.py ├── modular_exponentiation_test.py ├── modular_multiplicative_inverse_test.py ├── sieve_test.py ├── singly_linked_list_test.py └── unionfind_test.py ├── trees ├── binarysearchtree.py └── trie.py └── union_find ├── __init__.py └── unionfind.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.swp 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 2 | 3 | Everyone is permitted to copy and distribute verbatim or modified 4 | copies of this license document, and changing it is allowed as long 5 | as the name is changed. 6 | 7 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 8 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 9 | 10 | 0. You just DO WHAT THE FUCK YOU WANT TO. 11 | 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Algorithms in Python 2 | ====== 3 | 4 | Implementations of a few algorithms and datastructures for fun and profit! 5 | 6 | Completed 7 | --- 8 | - Karatsuba Multiplication 9 | - Basic Sorting 10 | - Rabin-Miller primality test 11 | - Sieve of Eratosthenes for prime numbers 12 | - Binary Search 13 | - Counting Inversions in an array 14 | - Selecting ith order statistic in an array 15 | - Graph datastructure (directed & undirected) 16 | - Graph Algos 17 | - Topological Sorting 18 | - Shortest hops 19 | - DFS 20 | - BFS 21 | - Connected Components 22 | - Dijkstra's Shortest Path - O(mlogn) 23 | - Prim's Minimum Cost Spanning Tree - O(mlogn) 24 | - Kruskal's Minimum Spanning Tree - O(mlogn) 25 | - Max k Clustering 26 | - Bellman Ford 27 | - Floyd Warshall 28 | - Johnson's Algorithm 29 | - Heap datastructure 30 | - Max heaps 31 | - Min heaps (priority queue) 32 | - Heapsort 33 | - Job Scheduling 34 | - [UnionFind](http://en.wikipedia.org/wiki/Disjoint-set_data_structure) Data Structure 35 | - Binary Search Tree 36 | - Kandane's Algorithm 37 | - Knapsack Problem (0/1 and unbounded) 38 | - Longest Increasing Subsequence 39 | - Longest Common Subsequence 40 | - Prefix Tries 41 | - Stack ADT (with example problems) 42 | - String Reverse 43 | - Parenthesis Matching 44 | - Infix to Postfix 45 | - Modular exponentiation 46 | - Modular multiplicative inverse 47 | 48 | 49 | Tests 50 | --- 51 | python -m tests.graph_test 52 | python -m tests.digraph_test 53 | python -m tests.graph_algorithms_test 54 | python -m tests.heap_test 55 | python -m tests.unionfind_test 56 | python -m tests.singly_linked_list_test 57 | python -m tests.modular_exponentiation_test 58 | python -m tests.modular_multiplicative_inverse_test 59 | -------------------------------------------------------------------------------- /dp/bellman_ford.py: -------------------------------------------------------------------------------- 1 | """ The bellman ford algorithm for calculating single source shortest 2 | paths - CLRS style """ 3 | graph = { 4 | 's' : {'t':6, 'y':7}, 5 | 't' : {'x':5, 'z':-4, 'y':8 }, 6 | 'y' : {'z':9, 'x':-3}, 7 | 'z' : {'x':7, 's': 2}, 8 | 'x' : {'t':-2} 9 | } 10 | 11 | INF = float('inf') 12 | 13 | dist = {} 14 | predecessor = {} 15 | 16 | def initialize_single_source(graph, s): 17 | for v in graph: 18 | dist[v] = INF 19 | predecessor[v] = None 20 | dist[s] = 0 21 | 22 | def relax(graph, u, v): 23 | if dist[v] > dist[u] + graph[u][v]: 24 | dist[v] = dist[u] + graph[u][v] 25 | predecessor[v] = u 26 | 27 | def bellman_ford(graph, s): 28 | initialize_single_source(graph, s) 29 | edges = [(u, v) for u in graph for v in graph[u].keys()] 30 | number_vertices = len(graph) 31 | for i in range(number_vertices-1): 32 | for (u, v) in edges: 33 | relax(graph, u, v) 34 | for (u, v) in edges: 35 | if dist[v] > dist[u] + graph[u][v]: 36 | return False # there exists a negative cycle 37 | return True 38 | 39 | def get_distances(graph, s): 40 | if bellman_ford(graph, s): 41 | return dist 42 | return "Graph contains a negative cycle" 43 | 44 | print get_distances(graph, 's') 45 | -------------------------------------------------------------------------------- /dp/coinchange.py: -------------------------------------------------------------------------------- 1 | """ 2 | Problem: http://www.algorithmist.com/index.php/Coin_Change 3 | """ 4 | def coinchange(total, coins): 5 | M = len(coins) 6 | table = [[0]*M for i in range(total+1)] 7 | for i in range(M): 8 | table[0][i] = 1 9 | 10 | for i in range(1, total+1): 11 | for j in range(M): 12 | # count of solutions excluding coin 13 | x = table[i][j-1] if j > 0 else 0 14 | 15 | # count of solutions including coin 16 | y = table[i-coins[j]][j] if i - coins[j] >= 0 else 0 17 | table[i][j] = x + y 18 | 19 | return table[total][M-1] 20 | 21 | if __name__ == "__main__": 22 | print coinchange(10, [2, 3, 5, 6]) # 5 23 | print coinchange(5, [2, 3, 5]) # 2 24 | print coinchange(4, [1, 2, 3]) # 4 25 | -------------------------------------------------------------------------------- /dp/dijkstra.py: -------------------------------------------------------------------------------- 1 | from heapq import heappush, heappop 2 | # graph = { 3 | # 's' : {'t':6, 'y':7}, 4 | # 't' : {'x':5, 'z':4, 'y':8 }, 5 | # 'y' : {'z':9, 'x':3}, 6 | # 'z' : {'x':7, 's': 2}, 7 | # 'x' : {'t':2} 8 | # } 9 | 10 | def read_graph(file): 11 | graph = dict() 12 | with open(file) as f: 13 | for l in f: 14 | (u, v, w) = l.split() 15 | if int(u) not in graph: 16 | graph[int(u)] = dict() 17 | graph[int(u)][int(v)] = int(w) 18 | return graph 19 | 20 | inf = float('inf') 21 | def dijkstra(graph, s): 22 | n = len(graph.keys()) 23 | dist = dict() 24 | Q = list() 25 | 26 | for v in graph: 27 | dist[v] = inf 28 | dist[s] = 0 29 | 30 | heappush(Q, (dist[s], s)) 31 | 32 | while Q: 33 | d, u = heappop(Q) 34 | if d < dist[u]: 35 | dist[u] = d 36 | for v in graph[u]: 37 | if dist[v] > dist[u] + graph[u][v]: 38 | dist[v] = dist[u] + graph[u][v] 39 | heappush(Q, (dist[v], v)) 40 | return dist 41 | 42 | graph = read_graph("graph.txt") 43 | print dijkstra(graph, 1) 44 | -------------------------------------------------------------------------------- /dp/floyd.py: -------------------------------------------------------------------------------- 1 | """ Floyd warshall in numpy and standard implementation """ 2 | from numpy import * 3 | inf = float('inf') 4 | graph = [ 5 | [0, 3, 8, inf, -4], 6 | [inf, 0, inf, 1, 7], 7 | [inf, 4, 0, inf, inf], 8 | [2, inf, -5, 0, inf], 9 | [inf, inf, inf, 6, 0] 10 | ] 11 | 12 | def make_matrix(file, n): 13 | graph = [[inf for i in range(n)] for i in range(n)] 14 | with open(file) as f: 15 | for l in f: 16 | (i, j, w) = l.split() 17 | graph[int(i)-1][int(j)-1] = int(w) 18 | return graph 19 | 20 | def floyd_warshall(graph): 21 | n = len(graph) 22 | D = graph 23 | for k in range(n): 24 | for i in range(n): 25 | for j in range(n): 26 | if i==j: 27 | D[i][j] = 0 28 | else: 29 | D[i][j] = min(D[i][j], D[i][k] + D[k][j]) 30 | return D 31 | 32 | def fastfloyd(D): 33 | _,n = D.shape 34 | for k in xrange(n): 35 | i2k = reshape(D[k,:],(1,n)) 36 | k2j = reshape(D[:,k],(n,1)) 37 | D = minimum(D,i2k+k2j) 38 | return D.min() if not any(D.diagonal() < 0) else None 39 | 40 | def get_min_dist(D): 41 | if negative_cost_cycle(D): 42 | return "Negative cost cycle" 43 | return min(i for d in D for i in d) 44 | 45 | def negative_cost_cycle(D): 46 | n = len(D) 47 | for i in range(n): 48 | if D[i][i] < 0: 49 | return True 50 | return False 51 | 52 | # print get_min_dist(floyd_warshall(graph)) 53 | n = 1000 54 | gr = make_matrix("g1.txt", n) 55 | #D = floyd_warshall(gr) 56 | print fastfloyd(array(gr)) 57 | # print get_min_dist(D) 58 | # print D 59 | -------------------------------------------------------------------------------- /dp/johnsons_apsp.py: -------------------------------------------------------------------------------- 1 | """ Johnson's algorithm for all-pairs shortest path problem. 2 | Reimplemented Bellman-Ford and Dijkstra's for clarity""" 3 | from heapq import heappush, heappop 4 | from datetime import datetime 5 | from copy import deepcopy 6 | graph = { 7 | 'a' : {'b':-2}, 8 | 'b' : {'c':-1}, 9 | 'c' : {'x':2, 'a':4, 'y':-3}, 10 | 'z' : {'x':1, 'y':-4}, 11 | 'x' : {}, 12 | 'y' : {}, 13 | } 14 | 15 | inf = float('inf') 16 | dist = {} 17 | 18 | def read_graph(file,n): 19 | graph = dict() 20 | with open(file) as f: 21 | for l in f: 22 | (u, v, w) = l.split() 23 | if int(u) not in graph: 24 | graph[int(u)] = dict() 25 | graph[int(u)][int(v)] = int(w) 26 | for i in range(n): 27 | if i not in graph: 28 | graph[i] = dict() 29 | return graph 30 | 31 | def dijkstra(graph, s): 32 | n = len(graph.keys()) 33 | dist = dict() 34 | Q = list() 35 | 36 | for v in graph: 37 | dist[v] = inf 38 | dist[s] = 0 39 | 40 | heappush(Q, (dist[s], s)) 41 | 42 | while Q: 43 | d, u = heappop(Q) 44 | if d < dist[u]: 45 | dist[u] = d 46 | for v in graph[u]: 47 | if dist[v] > dist[u] + graph[u][v]: 48 | dist[v] = dist[u] + graph[u][v] 49 | heappush(Q, (dist[v], v)) 50 | return dist 51 | 52 | def initialize_single_source(graph, s): 53 | for v in graph: 54 | dist[v] = inf 55 | dist[s] = 0 56 | 57 | def relax(graph, u, v): 58 | if dist[v] > dist[u] + graph[u][v]: 59 | dist[v] = dist[u] + graph[u][v] 60 | 61 | def bellman_ford(graph, s): 62 | initialize_single_source(graph, s) 63 | edges = [(u, v) for u in graph for v in graph[u].keys()] 64 | number_vertices = len(graph) 65 | for i in range(number_vertices-1): 66 | for (u, v) in edges: 67 | relax(graph, u, v) 68 | for (u, v) in edges: 69 | if dist[v] > dist[u] + graph[u][v]: 70 | return False # there exists a negative cycle 71 | return True 72 | 73 | def add_extra_node(graph): 74 | graph[0] = dict() 75 | for v in graph.keys(): 76 | if v != 0: 77 | graph[0][v] = 0 78 | 79 | def reweighting(graph_new): 80 | add_extra_node(graph_new) 81 | if not bellman_ford(graph_new, 0): 82 | # graph contains negative cycles 83 | return False 84 | for u in graph_new: 85 | for v in graph_new[u]: 86 | if u != 0: 87 | graph_new[u][v] += dist[u] - dist[v] 88 | del graph_new[0] 89 | return graph_new 90 | 91 | def johnsons(graph_new): 92 | graph = reweighting(graph_new) 93 | if not graph: 94 | return False 95 | final_distances = {} 96 | for u in graph: 97 | final_distances[u] = dijkstra(graph, u) 98 | 99 | for u in final_distances: 100 | for v in final_distances[u]: 101 | final_distances[u][v] += dist[v] - dist[u] 102 | return final_distances 103 | 104 | def compute_min(final_distances): 105 | return min(final_distances[u][v] for u in final_distances for v in final_distances[u]) 106 | 107 | if __name__ == "__main__": 108 | # graph = read_graph("graph.txt", 1000) 109 | graph_new = deepcopy(graph) 110 | t1 = datetime.utcnow() 111 | final_distances = johnsons(graph_new) 112 | if not final_distances: 113 | print "Negative cycle" 114 | else: 115 | print compute_min(final_distances) 116 | print datetime.utcnow() - t1 117 | -------------------------------------------------------------------------------- /dp/kadane.py: -------------------------------------------------------------------------------- 1 | """ 2 | Problem: The maximum subarray problem is the task of finding the 3 | contiguous subarray within a one-dimensional array of numbers 4 | (containing at least one positive number) which has the largest sum. 5 | 6 | Solution: 7 | The recurrence relation that we solve at each step is the following - 8 | 9 | Let S[i] = be the max value contigous subsequence till the ith element 10 | of the array. 11 | 12 | Then S[i] = max(A[i], A[i] + S[i - 1]) 13 | At each step, we have two options 14 | 1) We add the ith element to the sum till the i-1th elem 15 | 2) We start a new array starting at i 16 | 17 | We take a max of both these options and accordingly build up the array. 18 | """ 19 | def max_value_contigous_subsequence(arr): 20 | A = [arr[0]] + [0] * (len(arr) - 1) 21 | max_to_here = arr[0] 22 | for i in range(1, len(arr)): 23 | A[i] = max(arr[i], arr[i] + A[i-1]) 24 | max_to_here = max(max_to_here, A[i]) 25 | return max_to_here 26 | 27 | if __name__ == "__main__": 28 | x = [-2, -3, 4, -1, -2, 1, 5, -3] 29 | y = [-2, 1, -3, 4, -1, 2, 1, -5, 4] 30 | z = [-1, 3, -5, 4, 6, -1, 2, -7, 13, -3] 31 | print map(max_value_contigous_subsequence, [x, y, z]) 32 | -------------------------------------------------------------------------------- /dp/kp.py: -------------------------------------------------------------------------------- 1 | def read_data(data): 2 | weights = [] 3 | values = [] 4 | for d in data: 5 | weights.append(int(d.split()[1])) 6 | values.append(int(d.split()[0])) 7 | return (weights, values) 8 | 9 | def knapsack_rep(weights, values, W): 10 | """ knapsack with repetition """ 11 | k = [0]*(W + 1) 12 | for w in range(1, W+1): 13 | k[w] = max([k[w-i] + values[i] if weights[i]<=w else 0 for i in range(len(weights))]) 14 | return k[-1] 15 | 16 | def knapsack(weights, values, W): 17 | """ knapsack without repetition. Takes O(w) space 18 | Reference - http://books.google.co.in/books?id=u5DB7gck08YC&printsec=frontcover&dq=knapsack+problem&hl=en&sa=X&ei=1sbmUJSwDYWGrAeLi4GgCQ&ved=0CDUQ6AEwAA#v=onepage&q&f=true 19 | """ 20 | optimal_vals = [0]*(W+1) 21 | for j in range(0, len(weights)): 22 | for w in range(W, weights[j]-1, -1): 23 | if optimal_vals[w-weights[j]] + values[j] > optimal_vals[w]: 24 | optimal_vals[w] = optimal_vals[w-weights[j]] + values[j] 25 | return optimal_vals[-1] 26 | 27 | with open("kpdata2.txt") as f: 28 | (weights, values) = read_data(f) 29 | # print knapsack_rep(weights, values, 10000) 30 | #print knapsack(weights, values, 2000000) 31 | -------------------------------------------------------------------------------- /dp/kpdata.txt: -------------------------------------------------------------------------------- 1 | 16808 241486 2 | 50074 834558 3 | 8931 738037 4 | 27545 212860 5 | 77924 494349 6 | 64441 815107 7 | 84493 723724 8 | 7988 421316 9 | 82328 652893 10 | 78841 402599 11 | 44304 631607 12 | 17710 318556 13 | 29561 608119 14 | 93100 429390 15 | 51817 431959 16 | 99098 365899 17 | 13513 90282 18 | 23811 467558 19 | 80980 743542 20 | 36580 896948 21 | 11968 883369 22 | 1394 604449 23 | 25486 562244 24 | 25229 333236 25 | 40195 157443 26 | 35002 696933 27 | 16709 539123 28 | 15669 202584 29 | 88125 759690 30 | 9531 69730 31 | 27723 110467 32 | 28550 651058 33 | 97802 231944 34 | 40978 186803 35 | 8229 572887 36 | 60299 797491 37 | 28636 692529 38 | 23866 503157 39 | 39064 243688 40 | 39426 182032 41 | 24116 262772 42 | 75630 246257 43 | 46518 605917 44 | 30106 685556 45 | 19452 522241 46 | 82189 419114 47 | 99506 622799 48 | 6753 512332 49 | 36717 489578 50 | 54439 869423 51 | 51502 771067 52 | 83872 884103 53 | 11138 450309 54 | 53178 444804 55 | 22295 405168 56 | 21610 527012 57 | 59746 255432 58 | 53636 702071 59 | 98143 264607 60 | 27969 227173 61 | 261 471962 62 | 41595 807033 63 | 16396 471595 64 | 19114 520773 65 | 71007 21653 66 | 97943 882635 67 | 42083 801161 68 | 30768 806299 69 | 85696 402599 70 | 73672 858413 71 | 48591 122945 72 | 14739 256166 73 | 31617 350118 74 | 55641 783545 75 | 37336 518571 76 | 97973 17616 77 | 49096 640048 78 | 83455 74134 79 | 12290 610321 80 | 48906 141662 81 | 36124 589402 82 | 45814 638947 83 | 35239 751616 84 | 96221 533618 85 | 12367 850339 86 | 25227 670142 87 | 41364 699869 88 | 7845 269378 89 | 36551 198914 90 | 8624 98356 91 | 97386 122211 92 | 95273 646654 93 | 99248 231210 94 | 13497 806666 95 | 40624 153773 96 | 28145 427188 97 | 35736 90282 98 | 61626 23855 99 | 46043 585365 100 | 54680 654728 101 | 75245 593439 102 | 78819 117073 103 | 87693 903554 104 | 12992 357458 105 | 22670 469393 106 | 89554 642617 107 | 75826 568850 108 | 29663 532884 109 | 33809 531783 110 | 22762 171022 111 | 37336 833090 112 | 77996 467191 113 | 62768 784279 114 | 29875 716017 115 | 40557 519305 116 | 68873 549766 117 | 20095 435262 118 | 13756 171022 119 | 51408 709411 120 | 30194 111935 121 | 15681 116706 122 | 16856 669408 123 | 70964 512332 124 | 86677 892544 125 | 63250 836026 126 | 77845 418747 127 | 60809 531049 128 | 28652 532884 129 | 45204 478201 130 | 96532 514167 131 | 95420 866487 132 | 42010 780976 133 | 87930 258368 134 | 44055 202217 135 | 76738 877864 136 | 47318 901352 137 | 4201 634910 138 | 21565 69730 139 | 6649 16515 140 | 25551 203685 141 | 41977 835292 142 | 38555 736202 143 | 2505 215429 144 | 34749 905022 145 | 59379 171389 146 | 78210 744276 147 | 78554 485174 148 | 50448 394158 149 | 80774 697300 150 | 64306 324061 151 | 70054 724825 152 | 84631 377643 153 | 5401 191941 154 | 95371 695098 155 | 83017 107164 156 | 8156 807033 157 | 15558 218365 158 | 53179 670142 159 | 87358 219466 160 | 27879 757121 161 | 74820 194143 162 | 83134 660600 163 | 23721 531783 164 | 6634 32663 165 | 76032 528480 166 | 60590 884470 167 | 98878 569584 168 | 79359 553436 169 | 77255 80373 170 | 14916 269378 171 | 45481 776205 172 | 69956 608853 173 | 37108 143497 174 | 40001 213961 175 | 45036 344613 176 | 61114 472696 177 | 29594 539123 178 | 98355 675647 179 | 25358 809235 180 | 13730 649223 181 | 76564 218365 182 | 60918 373606 183 | 19420 63491 184 | 47748 105696 185 | 55396 600045 186 | 4474 913096 187 | 11749 417279 188 | 5659 576190 189 | 45407 777306 190 | 39825 916399 191 | 974 70097 192 | 46898 715650 193 | 3951 50646 194 | 88481 562978 195 | 13901 764828 196 | 62534 772535 197 | 17004 569217 198 | 20951 344246 199 | 58781 576557 200 | 41833 484073 201 | 33550 262405 202 | 6250 548665 203 | 66311 846302 204 | 984 237082 205 | 92041 86245 206 | 73651 451043 207 | 53230 873827 208 | 17747 340209 209 | 87231 470861 210 | 70777 354889 211 | 68245 560409 212 | 47697 456181 213 | 29065 364064 214 | 56599 853642 215 | 69941 86245 216 | 89552 891443 217 | 27206 238183 218 | 80419 708677 219 | 59650 713448 220 | 83321 593439 221 | 22013 800794 222 | 54789 811070 223 | 24009 64592 224 | 65325 643718 225 | 51258 547197 226 | 35691 770333 227 | 62411 360027 228 | 90554 384983 229 | 90961 242220 230 | 51404 456181 231 | 7308 263873 232 | 44911 44774 233 | 92800 210658 234 | 20172 702438 235 | 34415 522608 236 | 16642 883002 237 | 55349 229375 238 | 14316 229375 239 | 81446 587567 240 | 72221 702071 241 | 9240 681152 242 | 41306 439666 243 | 82254 569217 244 | 15167 44040 245 | 59305 492514 246 | 45899 274516 247 | 1721 647388 248 | 51581 173591 249 | 49353 713448 250 | 78699 803363 251 | 60812 626469 252 | 56892 885204 253 | 31729 70097 254 | 51662 300940 255 | 3121 193409 256 | 17159 448107 257 | 69628 266075 258 | 53712 737303 259 | 29595 767030 260 | 1728 778774 261 | 98796 623166 262 | 37443 173958 263 | 85007 28993 264 | 66925 438198 265 | 27098 28993 266 | 67104 736202 267 | 80109 49178 268 | 2282 51013 269 | 43711 456548 270 | 1906 245890 271 | 67782 447740 272 | 70792 346815 273 | 97866 424252 274 | 29537 643351 275 | 57126 605917 276 | 80168 124413 277 | 477 792353 278 | 21577 216530 279 | 77468 571419 280 | 27385 373973 281 | 25211 910160 282 | 28212 37434 283 | 19519 788316 284 | 21935 671977 285 | 80768 843366 286 | 49819 30461 287 | 11826 765562 288 | 58029 693263 289 | 78035 197813 290 | 51241 914564 291 | 86541 80006 292 | 90936 640415 293 | 48049 103127 294 | 87012 148268 295 | 53065 755286 296 | 17238 601146 297 | 70246 643351 298 | 70416 446272 299 | 24711 83676 300 | 63038 601146 301 | 68448 270846 302 | 41694 471962 303 | 67130 793454 304 | 33980 25323 305 | 95526 631974 306 | 74959 279287 307 | 11801 837127 308 | 87671 310482 309 | 80847 29727 310 | 9544 481504 311 | 85937 343145 312 | 97720 712347 313 | 91248 681519 314 | 28360 177628 315 | 45036 308647 316 | 83913 383148 317 | 45990 226072 318 | 63735 870891 319 | 520 608486 320 | 67046 170288 321 | 26662 853642 322 | 82820 815474 323 | 22904 485174 324 | 36799 171022 325 | 14005 75969 326 | 680 373239 327 | 7392 11377 328 | 64925 211025 329 | 57122 750148 330 | 77107 175426 331 | 51704 577658 332 | 71033 300206 333 | 61655 729596 334 | 42218 330300 335 | 57290 271580 336 | 84748 344246 337 | 89880 890342 338 | 1517 266809 339 | 82763 677482 340 | 49040 687391 341 | 5904 592338 342 | 10762 475632 343 | 61467 211025 344 | 44558 659866 345 | 26956 747579 346 | 21495 751983 347 | 92222 148635 348 | 2294 546096 349 | 42233 803730 350 | 34792 782811 351 | 49153 468292 352 | 73571 862083 353 | 27440 422784 354 | 35800 547931 355 | 91836 119642 356 | 46044 903187 357 | 74087 11744 358 | 39151 263873 359 | 18624 324428 360 | 9606 760057 361 | 37312 718953 362 | 77993 321125 363 | 38700 550133 364 | 80640 181665 365 | 25837 614358 366 | 82858 9542 367 | 83329 456548 368 | 74411 253964 369 | 85177 320391 370 | 72092 745377 371 | 84712 129551 372 | 91027 364798 373 | 10791 740973 374 | 59365 256900 375 | 24885 536554 376 | 41442 323694 377 | 16180 252863 378 | 94538 438198 379 | 87562 642250 380 | 74166 892911 381 | 57733 838228 382 | 47855 547197 383 | 9848 299105 384 | 40463 519672 385 | 43820 80006 386 | 13378 225338 387 | 43837 82942 388 | 32300 419114 389 | 34760 867588 390 | 74391 500955 391 | 30049 203685 392 | 59045 26424 393 | 40151 554904 394 | 41296 197079 395 | 95732 58720 396 | 28246 573254 397 | 46058 332135 398 | 93929 177995 399 | 62174 629038 400 | 9670 147534 401 | 4504 873827 402 | 18676 450676 403 | 24301 914197 404 | 40263 659132 405 | 68264 545729 406 | 24890 524443 407 | 76736 863551 408 | 31365 262772 409 | 50964 410306 410 | 85653 263506 411 | 57317 293233 412 | 61421 58353 413 | 7451 223136 414 | 78742 240385 415 | 99197 178729 416 | 2502 619863 417 | 93129 860248 418 | 77449 41104 419 | 17254 534352 420 | 69330 184601 421 | 33720 367367 422 | 38821 862817 423 | 81080 44407 424 | 94411 366266 425 | 66114 185702 426 | 98139 389020 427 | 46045 216163 428 | 98394 321859 429 | 981 538022 430 | 45597 208089 431 | 67371 397461 432 | 6544 836393 433 | 62853 536921 434 | 76825 856211 435 | 82381 835292 436 | 71858 673445 437 | 59499 253597 438 | 52846 637479 439 | 68510 375808 440 | 96850 896214 441 | 19291 907591 442 | 85744 97989 443 | 36386 749047 444 | 86276 493248 445 | 11152 843733 446 | 75252 74134 447 | 32784 804831 448 | 66438 447740 449 | 7699 873827 450 | 83138 347549 451 | 45879 875295 452 | 70343 314519 453 | 45961 662802 454 | 35141 228641 455 | 43567 609220 456 | 563 114504 457 | 41970 721889 458 | 12624 847770 459 | 17314 450309 460 | 27011 782811 461 | 76979 415811 462 | 7395 42939 463 | 87657 633075 464 | 61787 423518 465 | 54545 680051 466 | 8777 22754 467 | 74253 642984 468 | 75515 14680 469 | 95020 29360 470 | 66672 310482 471 | 56664 101292 472 | 84025 546463 473 | 16855 720788 474 | 14982 751249 475 | 86289 257267 476 | 8360 856578 477 | 55380 560776 478 | 19462 674913 479 | 8354 849238 480 | 85789 906857 481 | 69881 895847 482 | 38284 303142 483 | 237 855110 484 | 98568 353054 485 | 47940 451410 486 | 69652 48811 487 | 9267 640415 488 | 111 11010 489 | 86328 820245 490 | 25582 745377 491 | 7937 250661 492 | 45436 715283 493 | 66973 184234 494 | 19799 778040 495 | 74588 571786 496 | 69090 86245 497 | 29091 784646 498 | 66613 883369 499 | 72113 21653 500 | 51952 540591 501 | -------------------------------------------------------------------------------- /dp/lcs.py: -------------------------------------------------------------------------------- 1 | """ 2 | Problem : https://en.wikipedia.org/wiki/Longest_common_subsequence_problem 3 | """ 4 | 5 | def longest_common_subsequence(s1, s2): 6 | # lengths of strings s1 and s2 7 | m, n = len(s1), len(s2) 8 | # to cache the results 9 | cache = [[0 for j in range(n + 1)] for i in range(m + 1)] 10 | for i, character_s1 in enumerate(s1): 11 | for j, character_s2 in enumerate(s2): 12 | if character_s1 == character_s2: 13 | cache[i + 1][j + 1] = cache[i][j] + 1 14 | else: 15 | cache[i + 1][j + 1] = max(cache[i][j + 1], cache[i + 1][j]) 16 | # LCS is empty by default 17 | sequence = "" 18 | i, j = m, n 19 | # finding the sequence from cache 20 | while i >= 1 and j >= 1: 21 | if s1[i - 1] == s2[j - 1]: 22 | sequence += s1[i - 1] 23 | i, j = i - 1, j - 1 24 | elif cache[i - 1][j] > cache[i][j - 1]: 25 | i -= 1 26 | else: 27 | j -= 1 28 | # returns the length of LCS along with the sequence itself 29 | return (len(sequence), sequence[::-1]) 30 | 31 | if __name__ == "__main__": 32 | print(longest_common_subsequence("ABCXYZ","ACBCXZ")) 33 | -------------------------------------------------------------------------------- /dp/longest_subsequence.py: -------------------------------------------------------------------------------- 1 | """ 2 | Problem: http://www.geeksforgeeks.org/dynamic-programming-set-3-longest-increasing-subsequence/ 3 | """ 4 | def longest_increasing_subsequence(nums): 5 | # array used to store the length of the longest subsequence found 6 | cache = [1] * len(nums) 7 | 8 | # array used to store the location of the predecessor in the longest 9 | # subsequence. -1 by default 10 | location = [-1] * len(nums) 11 | 12 | for i in range(1, len(nums)): 13 | for j in range(0, i): 14 | if nums[i] > nums[j]: 15 | if cache[j] + 1 > cache[i]: 16 | cache[i] = cache[j] + 1 17 | location[i] = j 18 | 19 | # finding the max in the cache gives us the 20 | # answer - i.e. length of the LIS 21 | max_value = max(cache) 22 | 23 | # with the answer in hand, we need to build the solution 24 | # using the locations stored 25 | solution = [] 26 | i = cache.index(max_value) 27 | 28 | # we start with the max value i.e. the index of the 29 | # location where the max LIS exists and then 30 | # keep backtracking to build up the solution 31 | while location[i] > -1: 32 | solution.append(nums[i]) 33 | i = location[i] 34 | 35 | # when the loop ends, just append the starting element 36 | solution.append(nums[i]) 37 | 38 | # return the length of the LIS and the solution (in reverse) 39 | return max_value, solution[::-1] 40 | 41 | if __name__ == "__main__": 42 | assert longest_increasing_subsequence([3, 4, -1, 0, 6, 2, 3]) == (4, [-1, 0, 2, 3]) 43 | assert longest_increasing_subsequence([10, 22, 9, 33, 21, 50, 41, 60, 80]) == (6, [10, 22, 33, 50, 60, 80]) 44 | assert longest_increasing_subsequence([5,0,1,2,3,4,5,6,7,8,9,10,11,12, 2, 8, 10, 3, 6, 9, 7]) == (13, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]) 45 | 46 | -------------------------------------------------------------------------------- /dp/max_subsquare_matrix.py: -------------------------------------------------------------------------------- 1 | """ Given a binary matrix, find out the maximum size square sub-matrix with all 1s. 2 | For example, consider the below binary matrix. 3 | 0 1 1 0 1 4 | 1 1 0 1 0 5 | 0 1 1 1 0 6 | 1 1 1 1 0 7 | 1 1 1 1 1 8 | 0 0 0 0 0 9 | The maximum square sub-matrix with all set bits is 10 | 1 1 1 11 | 1 1 1 12 | 1 1 1 13 | """ 14 | from copy import deepcopy 15 | matrix = [[0,1,1,0,1],[1,1,0,1,0],[0,1,1,1,0], 16 | [1,1,1,1,0],[1,1,1,1,1],[0,0,0,0,0]] 17 | 18 | def find_sub_matrix_size(matrix): 19 | copy_matrix = deepcopy(matrix) 20 | for i in range(1, len(matrix)): 21 | for j in range(1, len(matrix[0])): 22 | if matrix[i][j] == 1: 23 | copy_matrix[i][j] = min(copy_matrix[i-1][j], 24 | copy_matrix[i][j-1], 25 | copy_matrix[i-1][j-1]) + 1 26 | else: 27 | copy_matrix[i][j] = 0 28 | return max([item for rows in copy_matrix for item in rows]) 29 | 30 | print find_sub_matrix_size(matrix) 31 | -------------------------------------------------------------------------------- /graphs/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prakhar1989/Algorithms/82e64ce9d451b33c1bce64a63276d6341a1f13b0/graphs/__init__.py -------------------------------------------------------------------------------- /graphs/clustering.py: -------------------------------------------------------------------------------- 1 | import os, sys 2 | sys.path.append(os.path.join(os.getcwd(), os.path.pardir)) 3 | from graph import graph 4 | from union_find.unionfind import UnionFind 5 | 6 | data = """1 2 6808 7 | 1 3 5250 8 | 1 4 74 9 | 1 5 3659 10 | 1 6 8931 11 | 1 7 1273 12 | 1 8 7545 13 | 1 9 879 14 | 1 10 7924 15 | 1 11 7710 16 | 1 12 4441 17 | 1 13 8166 18 | 1 14 4493 19 | 1 15 3043 20 | 1 16 7988 21 | 1 17 2504 22 | 1 18 2328 23 | 1 19 1730 24 | 1 20 8841 25 | 2 3 4902 26 | 2 4 812 27 | 2 5 6617 28 | 2 6 6892 29 | 2 7 8672 30 | 2 8 1729 31 | 2 9 6672 32 | 2 10 1662 33 | 2 11 7046 34 | 2 12 3121 35 | 2 13 241 36 | 2 14 7159 37 | 2 15 9454 38 | 2 16 9628 39 | 2 17 5351 40 | 2 18 3712 41 | 2 19 5564 42 | 2 20 9595 43 | 3 4 3782 44 | 3 5 1952 45 | 3 6 9231 46 | 3 7 3322 47 | 3 8 3214 48 | 3 9 5129 49 | 3 10 1951 50 | 3 11 8601 51 | 3 12 8960 52 | 3 13 9755 53 | 3 14 9993 54 | 3 15 5195 55 | 3 16 3331 56 | 3 17 8633 57 | 3 18 3560 58 | 3 19 8778 59 | 3 20 7780 60 | 4 5 5000 61 | 4 6 4521 62 | 4 7 4516 63 | 4 8 2578 64 | 4 9 9923 65 | 4 10 7143 66 | 4 11 3981 67 | 4 12 5882 68 | 4 13 6886 69 | 4 14 31 70 | 4 15 6326 71 | 4 16 2437 72 | 4 17 6995 73 | 4 18 3946 74 | 4 19 2603 75 | 4 20 3234 76 | 5 6 8696 77 | 5 7 6896 78 | 5 8 5665 79 | 5 9 5601 80 | 5 10 5119 81 | 5 11 5118 82 | 5 12 3724 83 | 5 13 1618 84 | 5 14 3755 85 | 5 15 9569 86 | 5 16 8588 87 | 5 17 4576 88 | 5 18 4914 89 | 5 19 8123 90 | 5 20 4158 91 | 6 7 2495 92 | 6 8 3894 93 | 6 9 3065 94 | 6 10 4564 95 | 6 11 5430 96 | 6 12 5502 97 | 6 13 6873 98 | 6 14 6941 99 | 6 15 8054 100 | 6 16 4330 101 | 6 17 2233 102 | 6 18 2281 103 | 6 19 9360 104 | 6 20 5475 105 | 7 8 2739 106 | 7 9 4442 107 | 7 10 4843 108 | 7 11 8503 109 | 7 12 5466 110 | 7 13 5370 111 | 7 14 8012 112 | 7 15 3909 113 | 7 16 3503 114 | 7 17 1844 115 | 7 18 5374 116 | 7 19 7081 117 | 7 20 9837 118 | 8 9 3060 119 | 8 10 5421 120 | 8 11 5098 121 | 8 12 4080 122 | 8 13 3019 123 | 8 14 8318 124 | 8 15 9158 125 | 8 16 2031 126 | 8 17 722 127 | 8 18 4052 128 | 8 19 1072 129 | 8 20 9529 130 | 9 10 7569 131 | 9 11 563 132 | 9 12 5705 133 | 9 13 9597 134 | 9 14 4813 135 | 9 15 6965 136 | 9 16 8810 137 | 9 17 5046 138 | 9 18 17 139 | 9 19 1710 140 | 9 20 2262 141 | 10 11 3444 142 | 10 12 370 143 | 10 13 8675 144 | 10 14 5780 145 | 10 15 6209 146 | 10 16 2586 147 | 10 17 9086 148 | 10 18 4323 149 | 10 19 1212 150 | 10 20 79 151 | 11 12 1975 152 | 11 13 1665 153 | 11 14 3396 154 | 11 15 9439 155 | 11 16 594 156 | 11 17 5821 157 | 11 18 6006 158 | 11 19 836 159 | 11 20 2450 160 | 12 13 6821 161 | 12 14 5835 162 | 12 15 8470 163 | 12 16 3141 164 | 12 17 9413 165 | 12 18 760 166 | 12 19 3568 167 | 12 20 7424 168 | 13 14 4357 169 | 13 15 6374 170 | 13 16 7456 171 | 13 17 6025 172 | 13 18 9458 173 | 13 19 3064 174 | 13 20 9874 175 | 14 15 9946 176 | 14 16 6500 177 | 14 17 2476 178 | 14 18 4187 179 | 14 19 6686 180 | 14 20 9103 181 | 15 16 8910 182 | 15 17 5182 183 | 15 18 4761 184 | 15 19 8506 185 | 15 20 4676 186 | 16 17 881 187 | 16 18 4769 188 | 16 19 2903 189 | 16 20 66 190 | 17 18 2747 191 | 17 19 7119 192 | 17 20 2874 193 | 18 19 6302 194 | 18 20 7382 195 | 19 20 1143 196 | """ 197 | 198 | def max_k_clustering(gr, k): 199 | sorted_edges = sorted(gr.get_edge_weights()) 200 | uf = UnionFind() 201 | #initialize each node as its cluster 202 | for n in gr.nodes(): 203 | uf.insert(n) 204 | for (w, (u, v)) in sorted_edges: 205 | if uf.count_groups() <= k: 206 | return uf.get_sets() 207 | if uf.get_leader(u) != uf.get_leader(v): 208 | uf.make_union(uf.get_leader(u), uf.get_leader(v)) 209 | 210 | def compute_spacing(c1, c2): 211 | min = float('inf') 212 | for n in c1: 213 | for v in c2: 214 | cost = gr.get_edge_weight((n, v)) 215 | if cost < min: 216 | min = cost 217 | return min 218 | 219 | def get_max_spacing(clusters): 220 | min = float('inf') 221 | for u in clusters: 222 | for v in clusters: 223 | if u!= v: 224 | spacing = compute_spacing(u,v) 225 | if spacing < min: 226 | min = spacing 227 | return min 228 | 229 | if __name__ == "__main__": 230 | edges = [l.split() for l in data.splitlines()] 231 | gr = graph() 232 | 233 | for (u, v, w) in edges: 234 | if u not in gr.nodes(): 235 | gr.add_node(u) 236 | if v not in gr.nodes(): 237 | gr.add_node(v) 238 | gr.add_edge((u, v), int(w)) 239 | 240 | print "Min Spacing - %s " % (get_max_spacing(max_k_clustering(gr, 4))) 241 | -------------------------------------------------------------------------------- /graphs/digraph.py: -------------------------------------------------------------------------------- 1 | from graph import graph 2 | from copy import deepcopy 3 | class digraph(graph): 4 | """ 5 | Directed Graph class - made of nodes and edges 6 | 7 | methods: add_edge, add_edges, add_node, add_nodes, has_node, 8 | has_edge, nodes, edges, neighbors, del_node, del_edge, node_order, 9 | set_edge_weight, get_edge_weight, 10 | """ 11 | 12 | DEFAULT_WEIGHT = 1 13 | DIRECTED = True 14 | 15 | def __init__(self): 16 | self.node_neighbors = {} 17 | 18 | def __str__(self): 19 | return "Directed Graph \nNodes: %s \nEdges: %s" % (self.nodes(), self.edges()) 20 | 21 | def add_edge(self, edge, wt=DEFAULT_WEIGHT, label=""): 22 | """ 23 | Add an edge to the graph connecting two nodes. 24 | An edge, here, is a pair of node like C(m, n) or a tuple 25 | with m as head and n as tail : m -> n 26 | """ 27 | u, v = edge 28 | if (v not in self.node_neighbors[u]): 29 | self.node_neighbors[u][v] = wt 30 | else: 31 | raise Exception("Edge (%s, %s) already added in the graph" % (u, v)) 32 | 33 | def del_edge(self, edge): 34 | """ 35 | Deletes an edge from a graph. An edge, here, is a pair like 36 | C(m,n) or a tuple 37 | """ 38 | u, v = edge 39 | if not self.has_edge(edge): 40 | raise Exception("Edge (%s, %s) not an existing edge" % (u, v)) 41 | del self.node_neighbors[u][v] 42 | 43 | def del_node(self, node): 44 | """ 45 | Deletes a node from a graph 46 | """ 47 | for each in list(self.neighbors(node)): 48 | if (each != node): 49 | self.del_edge((node, each)) 50 | for n in self.nodes(): 51 | if self.has_edge((n, node)): 52 | self.del_edge((n, node)) 53 | del(self.node_neighbors[node]) 54 | 55 | def get_transpose(self): 56 | """ Returns the transpose of the graph 57 | with edges reversed and nodes same """ 58 | digr = deepcopy(self) 59 | for (u, v) in self.edges(): 60 | digr.del_edge((u, v)) 61 | digr.add_edge((v, u)) 62 | return digr 63 | -------------------------------------------------------------------------------- /graphs/eulerian_tour.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # Find Eulerian Tour 3 | # 4 | # Write a program that takes in a graph 5 | # represented as a list of tuples 6 | # and return a list of nodes that 7 | # you would follow on an Eulerian Tour 8 | # 9 | # For example, if the input graph was 10 | # [(1, 2), (2, 3), (3, 1)] 11 | # A possible Eulerian tour would be [1, 2, 3, 1] 12 | 13 | def get_a_tour(): 14 | '''This function returns a possible tour in the current graph and removes the edges included in that tour, from the graph.''' 15 | global graph 16 | 17 | nodes_degree = {} # Creating a {node: degree} dictionary for current graph. 18 | for edge in graph: 19 | a, b = edge[0], edge[1] 20 | nodes_degree[a] = nodes_degree.get(a, 0) + 1 21 | nodes_degree[b] = nodes_degree.get(b, 0) + 1 22 | 23 | tour =[] # Finding a tour in the current graph. 24 | loop = enumerate(nodes_degree) 25 | while True: 26 | try: 27 | l = loop.__next__() 28 | index = l[0] 29 | node = l[1] 30 | degree = nodes_degree[node] 31 | try: 32 | if (tour[-1], node) in graph or (node, tour[-1]) in graph: 33 | tour.append(node) 34 | try: 35 | graph.remove((tour[-2], tour[-1])) 36 | nodes_degree[tour[-1]] -= 1 # Updating degree of nodes in the graph, not required but for the sake of completeness. 37 | nodes_degree[tour[-2]] -= 1 # Can also be used to check the correctness of program. In the end all degrees must zero. 38 | except ValueError: 39 | graph.remove((tour[-1], tour[-2])) 40 | nodes_degree[tour[-1]] -= 1 41 | nodes_degree[tour[-2]] -= 1 42 | except IndexError: 43 | tour.append(node) 44 | except StopIteration: 45 | loop = enumerate(nodes_degree) 46 | 47 | if len(tour) > 2: 48 | if tour[0] == tour[-1]: 49 | return tour 50 | 51 | def get_eulerian_tour(): 52 | '''This function returns a Eulerian Tour for the input graph.''' 53 | global graph 54 | tour = get_a_tour() 55 | 56 | if graph: # If stuck at the beginning, finding additional tour in the graph. 57 | loop = enumerate(tour[: -1]) 58 | l = loop.__next__() 59 | i = l[0] 60 | node = l[1] 61 | try: 62 | while True: 63 | if node in list(zip(*graph))[0] or node in list(zip(*graph))[1]: 64 | t = get_a_tour() # Retreivng the additional tour 65 | j = t.index(node) 66 | tour = tour[ : i] + t[j:-1] + t[ :j+1] + tour[i+1: ] # Joining the two tours. 67 | if not graph: # Found Eulerian Tour 68 | return tour # Returning the Eulerian Tour 69 | loop = enumerate(tour[: -1]) # Still stuck? Looping back to search for another tour. 70 | l = loop.__next__() 71 | i = l[0] 72 | node = l[1] 73 | except StopIteration: # Oops! seems like the vertices in the current tour cannot connect to rest of the edges in the graph. 74 | print("Your graph doesn't seem to be connected") 75 | exit() 76 | else: # Found the Eulerian Tour in the very first call. Lucky Enough! 77 | return tour 78 | 79 | # Sample inputs 80 | # graph = [(1, 2), (1, 3), (2, 3), (2, 4), (2, 6), (3, 4), (3, 5), (4, 5), (4, 6)] 81 | # graph = [(1, 2), (1, 3), (2, 3)] 82 | # graph = [(1, 2), (1, 3), (2, 3), (2, 4), (2, 6), (3, 4), (3, 5), (4, 5), (4, 6), (9, 10), (10, 11), (11, 9)] 83 | # graph = [(1, 2), (1, 3), (2, 3), (2, 4), (2, 6), (3, 4), (3, 5), (4, 5), (4, 6), (2, 7), (7, 8), (8, 2)] 84 | # graph = [(1, 2), (1, 3), (2, 3), (2, 4), (2, 6), (3, 4), (3, 5), (4, 5), (4, 6), (1, 5), (5, 6), (1, 6)] 85 | # graph = [(1, 2), (2, 3), (3, 1), (3, 4), (4, 3)] 86 | # graph = [(0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 3)] 87 | # graph = [(2, 6), (4, 2), (5, 4), (6, 5), (6, 8), (7, 9), (8, 7), (9, 6)] 88 | 89 | # creating a {node: degree} dictionary 90 | nodes_degree = {} 91 | for edge in graph: 92 | a, b = edge[0], edge[1] 93 | nodes_degree[a] = nodes_degree.get(a, 0) + 1 94 | nodes_degree[b] = nodes_degree.get(b, 0) + 1 95 | 96 | #checking degree 97 | degrees = nodes_degree.values() # remember it return a view 98 | for degree in degrees: 99 | if degree % 2: 100 | print("Your graph have one or more nodes with odd degrees. Hence an Eulerian Tour is impossible.") 101 | exit() 102 | 103 | #finding Eulerian Tour 104 | tour = get_eulerian_tour() 105 | print(tour) 106 | -------------------------------------------------------------------------------- /graphs/graph.py: -------------------------------------------------------------------------------- 1 | class graph(object): 2 | """ 3 | Graph class - made of nodes and edges 4 | 5 | methods: add_edge, add_edges, add_node, add_nodes, has_node, 6 | has_edge, nodes, edges, neighbors, del_node, del_edge, node_order, 7 | set_edge_weight, get_edge_weight 8 | """ 9 | 10 | DEFAULT_WEIGHT = 1 11 | DIRECTED = False 12 | 13 | def __init__(self): 14 | self.node_neighbors = {} 15 | 16 | def __str__(self): 17 | return "Undirected Graph \nNodes: %s \nEdges: %s" % (self.nodes(), self.edges()) 18 | 19 | def add_nodes(self, nodes): 20 | """ 21 | Takes a list of nodes as input and adds these to a graph 22 | """ 23 | for node in nodes: 24 | self.add_node(node) 25 | 26 | def add_node(self, node): 27 | """ 28 | Adds a node to the graph 29 | """ 30 | if node not in self.node_neighbors: 31 | self.node_neighbors[node] = {} 32 | else: 33 | raise Exception("Node %s is already in graph" % node) 34 | 35 | def has_node(self, node): 36 | """ 37 | Returns boolean to indicate whether a node exists in the graph 38 | """ 39 | return node in self.node_neighbors 40 | 41 | def add_edge(self, edge, wt=DEFAULT_WEIGHT, label=""): 42 | """ 43 | Add an edge to the graph connecting two nodes. 44 | An edge, here, is a pair of node like C(m, n) or a tuple 45 | """ 46 | u, v = edge 47 | if (v not in self.node_neighbors[u] and u not in self.node_neighbors[v]): 48 | self.node_neighbors[u][v] = wt 49 | if (u!=v): 50 | self.node_neighbors[v][u] = wt 51 | else: 52 | raise Exception("Edge (%s, %s) already added in the graph" % (u, v)) 53 | 54 | def add_edges(self, edges): 55 | """ Adds multiple edges in one go. Edges, here, is a list of 56 | tuples""" 57 | for edge in edges: 58 | self.add_edge(edge) 59 | 60 | def nodes(self): 61 | """ 62 | Returns a list of nodes in the graph 63 | """ 64 | return self.node_neighbors.keys() 65 | 66 | def has_edge(self, edge): 67 | """ 68 | Returns a boolean to indicate whether an edge exists in the 69 | graph. An edge, here, is a pair of node like C(m, n) or a tuple 70 | """ 71 | u, v = edge 72 | return v in self.node_neighbors.get(u, []) 73 | 74 | def neighbors(self, node): 75 | """ 76 | Returns a list of neighbors for a node 77 | """ 78 | if not self.has_node(node): 79 | raise "Node %s not in graph" % node 80 | return self.node_neighbors[node].keys() 81 | 82 | def del_node(self, node): 83 | """ 84 | Deletes a node from a graph 85 | """ 86 | for each in list(self.neighbors(node)): 87 | if (each != node): 88 | self.del_edge((each, node)) 89 | del(self.node_neighbors[node]) 90 | 91 | def del_edge(self, edge): 92 | """ 93 | Deletes an edge from a graph. An edge, here, is a pair like 94 | C(m,n) or a tuple 95 | """ 96 | u, v = edge 97 | if not self.has_edge(edge): 98 | raise Exception("Edge (%s, %s) not an existing edge" % (u, v)) 99 | del self.node_neighbors[u][v] 100 | if (u!=v): 101 | del self.node_neighbors[v][u] 102 | 103 | def node_order(self, node): 104 | """ 105 | Return the order or degree of a node 106 | """ 107 | return len(self.neighbors(node)) 108 | 109 | 110 | def edges(self): 111 | """ 112 | Returns a list of edges in the graph 113 | """ 114 | edge_list = [] 115 | for node in self.nodes(): 116 | edges = [(node, each) for each in self.neighbors(node)] 117 | edge_list.extend(edges) 118 | return edge_list 119 | 120 | # Methods for setting properties on nodes and edges 121 | def set_edge_weight(self, edge, wt): 122 | """Set the weight of the edge """ 123 | u, v = edge 124 | if not self.has_edge(edge): 125 | raise Exception("Edge (%s, %s) not an existing edge" % (u, v)) 126 | self.node_neighbors[u][v] = wt 127 | if u != v: 128 | self.node_neighbors[v][u] = wt 129 | 130 | def get_edge_weight(self, edge): 131 | """Returns the weight of an edge """ 132 | u, v = edge 133 | if not self.has_edge((u, v)): 134 | raise Exception("%s not an existing edge" % edge) 135 | return self.node_neighbors[u].get(v, self.DEFAULT_WEIGHT) 136 | 137 | def get_edge_weights(self): 138 | """ Returns a list of all edges with their weights """ 139 | edge_list = [] 140 | unique_list = {} 141 | for u in self.nodes(): 142 | for v in self.neighbors(u): 143 | if u not in unique_list.get(v, set()): 144 | edge_list.append((self.node_neighbors[u][v], (u, v))) 145 | unique_list.setdefault(u, set()).add(v) 146 | return edge_list 147 | -------------------------------------------------------------------------------- /graphs/graph_algorithms.py: -------------------------------------------------------------------------------- 1 | from collections import deque 2 | from copy import deepcopy 3 | from union_find.unionfind import UnionFind 4 | import heapq 5 | 6 | def BFS(gr, s): 7 | """ Breadth first search 8 | Returns a list of nodes that are "findable" from s """ 9 | if not gr.has_node(s): 10 | raise Exception("Node %s not in graph" % s) 11 | nodes_explored = set([s]) 12 | q = deque([s]) 13 | while len(q)!=0: 14 | node = q.popleft() 15 | for each in gr.neighbors(node): 16 | if each not in nodes_explored: 17 | nodes_explored.add(each) 18 | q.append(each) 19 | return nodes_explored 20 | 21 | def shortest_hops(gr, s): 22 | """ Finds the shortest number of hops required 23 | to reach a node from s. Returns a dict with mapping: 24 | destination node from s -> no. of hops 25 | """ 26 | if not gr.has_node(s): 27 | raise Exception("Node %s is not in graph" % s) 28 | else: 29 | dist = {} 30 | q = deque([s]) 31 | nodes_explored = set([s]) 32 | for n in gr.nodes(): 33 | if n == s: dist[n] = 0 34 | else: dist[n] = float('inf') 35 | while len(q) != 0: 36 | node = q.popleft() 37 | for each in gr.neighbors(node): 38 | if each not in nodes_explored: 39 | nodes_explored.add(each) 40 | q.append(each) 41 | dist[each] = dist[node] + 1 42 | return dist 43 | 44 | def undirected_connected_components(gr): 45 | """ Returns a list of connected components 46 | in an undirected graph """ 47 | if gr.DIRECTED: 48 | raise Exception("This method works only with a undirected graph") 49 | explored = set([]) 50 | con_components = [] 51 | for node in gr.nodes(): 52 | if node not in explored: 53 | reachable_nodes = BFS(gr, node) 54 | con_components.append(reachable_nodes) 55 | explored |= reachable_nodes 56 | return con_components 57 | 58 | def DFS(gr, s): 59 | """ Depth first search wrapper """ 60 | path = set([]) 61 | depth_first_search(gr, s, path) 62 | return path 63 | 64 | def depth_first_search(gr, s, path): 65 | """ Depth first search 66 | Returns a list of nodes "findable" from s """ 67 | if s in path: return False 68 | path.add(s) 69 | for each in gr.neighbors(s): 70 | if each not in path: 71 | depth_first_search(gr, each, path) 72 | 73 | def topological_ordering(digr_ori): 74 | """ Returns a topological ordering for a 75 | acyclic directed graph """ 76 | if not digr_ori.DIRECTED: 77 | raise Exception("%s is not a directed graph" % digr) 78 | digr = deepcopy(digr_ori) 79 | ordering = [] 80 | n = len(digr.nodes()) 81 | while n > 0: 82 | sink_node = find_sink_node(digr) 83 | ordering.append((sink_node, n)) 84 | digr.del_node(sink_node) 85 | n -= 1 86 | return ordering 87 | 88 | def find_sink_node(digr): 89 | """ Finds a sink node (node with all incoming arcs) 90 | in the directed graph. Valid for a acyclic graph only """ 91 | # first node is taken as a default 92 | node = digr.nodes()[0] 93 | while digr.neighbors(node): 94 | node = digr.neighbors(node)[0] 95 | return node 96 | 97 | def directed_connected_components(digr): 98 | """ Returns a list of strongly connected components 99 | in a directed graph using Kosaraju's two pass algorithm """ 100 | if not digr.DIRECTED: 101 | raise Exception("%s is not a directed graph" % digr) 102 | finishing_times = DFS_loop(digr.get_transpose()) 103 | # use finishing_times in descending order 104 | nodes_explored, connected_components = [], [] 105 | for node in finishing_times[::-1]: 106 | component = [] 107 | outer_dfs(digr, node, nodes_explored, component) 108 | if component: 109 | nodes_explored += component 110 | connected_components.append(component) 111 | return connected_components 112 | 113 | def outer_dfs(digr, node, nodes_explored, path): 114 | if node in path or node in nodes_explored: 115 | return False 116 | path.append(node) 117 | for each in digr.neighbors(node): 118 | if each not in path or each not in nodes_explored: 119 | outer_dfs(digr, each, nodes_explored, path) 120 | 121 | def DFS_loop(digr): 122 | """ Core DFS loop used to find strongly connected components 123 | in a directed graph """ 124 | node_explored = set([]) # list for keeping track of nodes explored 125 | finishing_times = [] # list for adding nodes based on their finishing times 126 | for node in digr.nodes(): 127 | if node not in node_explored: 128 | leader_node = node 129 | inner_DFS(digr, node, node_explored, finishing_times) 130 | return finishing_times 131 | 132 | def inner_DFS(digr, node, node_explored, finishing_times): 133 | """ Inner DFS used in DFS loop method """ 134 | node_explored.add(node) # mark explored 135 | for each in digr.neighbors(node): 136 | if each not in node_explored: 137 | inner_DFS(digr, each, node_explored, finishing_times) 138 | global finishing_counter 139 | # adds nodes based on increasing order of finishing times 140 | finishing_times.append(node) 141 | 142 | def shortest_path(digr, s): 143 | """ Finds the shortest path from s to every other vertex findable 144 | from s using Dijkstra's algorithm in O(mlogn) time. Uses heaps 145 | for super fast implementation """ 146 | nodes_explored = set([s]) 147 | nodes_unexplored = DFS(digr, s) # all accessible nodes from s 148 | nodes_unexplored.remove(s) 149 | dist = {s:0} 150 | node_heap = [] 151 | 152 | for n in nodes_unexplored: 153 | min = compute_min_dist(digr, n, nodes_explored, dist) 154 | heapq.heappush(node_heap, (min, n)) 155 | 156 | while len(node_heap) > 0: 157 | min_dist, nearest_node = heapq.heappop(node_heap) 158 | dist[nearest_node] = min_dist 159 | nodes_explored.add(nearest_node) 160 | nodes_unexplored.remove(nearest_node) 161 | 162 | # recompute keys for just popped node 163 | for v in digr.neighbors(nearest_node): 164 | if v in nodes_unexplored: 165 | for i in range(len(node_heap)): 166 | if node_heap[i][1] == v: 167 | node_heap[i] = (compute_min_dist(digr, v, nodes_explored, dist), v) 168 | heapq.heapify(node_heap) 169 | 170 | return dist 171 | 172 | def compute_min_dist(digr, n, nodes_explored, dist): 173 | """ Computes the min dist of node n from a set of 174 | nodes explored in digr, using dist dict. Used in shortest path """ 175 | min = float('inf') 176 | for v in nodes_explored: 177 | if digr.has_edge((v, n)): 178 | d = dist[v] + digr.get_edge_weight((v, n)) 179 | if d < min: min = d 180 | return min 181 | 182 | def minimum_spanning_tree(gr): 183 | """ Uses prim's algorithm to return the minimum 184 | cost spanning tree in a undirected connected graph. 185 | Works only with undirected and connected graphs """ 186 | s = gr.nodes()[0] 187 | nodes_explored = set([s]) 188 | nodes_unexplored = gr.nodes() 189 | nodes_unexplored.remove(s) 190 | min_cost, node_heap = 0, [] 191 | 192 | #computes the key for each vertex in unexplored 193 | for n in nodes_unexplored: 194 | min = compute_key(gr, n, nodes_explored) 195 | heapq.heappush(node_heap, (min, n)) 196 | 197 | while len(nodes_unexplored) > 0: 198 | # adds the cheapest to "explored" 199 | node_cost, min_node = heapq.heappop(node_heap) 200 | min_cost += node_cost 201 | nodes_explored.add(min_node) 202 | nodes_unexplored.remove(min_node) 203 | 204 | # recompute keys for neighbors of deleted node 205 | for v in gr.neighbors(min_node): 206 | if v in nodes_unexplored: 207 | for i in range(len(node_heap)): 208 | if node_heap[i][1] == v: 209 | node_heap[i] = (compute_key(gr, v, nodes_explored), v) 210 | heapq.heapify(node_heap) 211 | return min_cost 212 | 213 | def compute_key(gr, n, nodes_explored): 214 | """ computes minimum key for node n from a set of nodes_explored 215 | in graph gr. Used in Prim's implementation """ 216 | min = float('inf') 217 | for v in gr.neighbors(n): 218 | if v in nodes_explored: 219 | w = gr.get_edge_weight((n, v)) 220 | if w < min: min = w 221 | return min 222 | 223 | def kruskal_MST(gr): 224 | """ computes minimum cost spanning tree in a undirected, 225 | connected graph using Kruskal's MST. Uses union-find data structure 226 | for running times of O(mlogn) """ 227 | sorted_edges = sorted(gr.get_edge_weights()) 228 | uf = UnionFind() 229 | min_cost = 0 230 | for (w, (u, v)) in sorted_edges: 231 | if (not uf.get_leader(u) and not uf.get_leader(v)) \ 232 | or (uf.get_leader(u) != uf.get_leader(v)): 233 | uf.insert(u, v) 234 | min_cost += w 235 | return min_cost 236 | 237 | def max_k_clustering(gr, k): 238 | sorted_edges = sorted(gr.get_edge_weights()) 239 | uf = UnionFind() 240 | #initialize each node as its cluster 241 | for n in gr.nodes(): 242 | uf.insert(n) 243 | for (w, (u, v)) in sorted_edges: 244 | if uf.count_groups() <= k: 245 | return uf.get_sets() 246 | if uf.get_leader(u) != uf.get_leader(v): 247 | uf.make_union(uf.get_leader(u), uf.get_leader(v)) 248 | 249 | def compute_spacing(c1, c2): 250 | min = float('inf') 251 | for n in c1: 252 | for v in c2: 253 | cost = gr.get_edge_weight((n, v)) 254 | if cost < min: 255 | min = cost 256 | return min 257 | 258 | def get_max_spacing(clusters): 259 | min = float('inf') 260 | for u in clusters: 261 | for v in clusters: 262 | if u!= v: 263 | spacing = compute_spacing(u,v) 264 | if spacing < min: 265 | min = spacing 266 | return min 267 | -------------------------------------------------------------------------------- /heaps/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prakhar1989/Algorithms/82e64ce9d451b33c1bce64a63276d6341a1f13b0/heaps/__init__.py -------------------------------------------------------------------------------- /heaps/heapsort.py: -------------------------------------------------------------------------------- 1 | from minheap import minheap 2 | import random 3 | 4 | def heapsort(nums): 5 | h = minheap(nums) 6 | return [h.heappop() for i in range(h.max_elements())] 7 | 8 | if __name__ == "__main__": 9 | a = [random.choice(range(100)) for i in range(40)] 10 | print heapsort(a) == sorted(a) 11 | -------------------------------------------------------------------------------- /heaps/maxheap.py: -------------------------------------------------------------------------------- 1 | from minheap import minheap 2 | class maxheap(minheap): 3 | """ 4 | Heap class - made of keys and items 5 | methods: build_heap, heappush, heappop 6 | """ 7 | 8 | MAX_HEAP = True 9 | 10 | def __str__(self): 11 | return "Max-heap with %s items" % (len(self.heap)) 12 | 13 | def heapify(self, i): 14 | l = self.leftchild(i) 15 | r = self.rightchild(i) 16 | largest = i 17 | if l < self.max_elements() and self.heap[l] > self.heap[largest]: 18 | largest = l 19 | if r < self.max_elements() and self.heap[r] > self.heap[largest]: 20 | largest = r 21 | if largest != i: 22 | self.heap[i], self.heap[largest] = self.heap[largest], self.heap[i] 23 | self.heapify(largest) 24 | 25 | def heappush(self, x): 26 | """ Adds a new item x in the heap""" 27 | i = len(self.heap) 28 | self.heap.append(x) 29 | parent = self.parent(i) 30 | while parent != -1 and self.heap[i] > self.heap[parent]: 31 | self.heap[i], self.heap[parent] = self.heap[parent], self.heap[i] 32 | i = parent 33 | parent = self.parent(i) 34 | -------------------------------------------------------------------------------- /heaps/minheap.py: -------------------------------------------------------------------------------- 1 | import math 2 | class minheap(object): 3 | """ 4 | Heap class - made of keys and items 5 | methods: build_heap, heappush, heappop 6 | """ 7 | 8 | MIN_HEAP = True 9 | 10 | def __init__(self, nums=None): 11 | self.heap = [] 12 | if nums: 13 | self.build_heap(nums) 14 | 15 | def __str__(self): 16 | return "Min-heap with %s items" % (len(self.heap)) 17 | 18 | def max_elements(self): 19 | return len(self.heap) 20 | 21 | def height(self): 22 | return math.ceil(math.log(len(self.heap)) / math.log(2)) 23 | 24 | def is_leaf(self, i): 25 | """ returns True if i is a leaf node """ 26 | return i > int(math.ceil((len(self.heap) - 2) / 2.0)) 27 | 28 | def parent(self, i): 29 | if i == 0: 30 | return -1 31 | elif i % 2 != 0: # odd 32 | return (i - 1) / 2 33 | return (i - 2) / 2 34 | 35 | def leftchild(self, i): 36 | return 2 * i + 1 37 | 38 | def rightchild(self, i): 39 | return 2 * i + 2 40 | 41 | def heapify(self, i): 42 | l = self.leftchild(i) 43 | r = self.rightchild(i) 44 | smallest = i 45 | if l < self.max_elements() and self.heap[l] < self.heap[smallest]: 46 | smallest = l 47 | if r < self.max_elements() and self.heap[r] < self.heap[smallest]: 48 | smallest = r 49 | if smallest != i: 50 | self.heap[i], self.heap[smallest] = self.heap[smallest], self.heap[i] 51 | self.heapify(smallest) 52 | 53 | def build_heap(self, elem): 54 | """ transforms a list of elements into a heap 55 | in linear time """ 56 | self.heap = elem[:] 57 | last_leaf = self.parent(len(self.heap)) 58 | for i in range(last_leaf, -1, -1): 59 | self.heapify(i) 60 | 61 | def heappush(self, x): 62 | """ Adds a new item x in the heap""" 63 | i = len(self.heap) 64 | self.heap.append(x) 65 | parent = self.parent(i) 66 | while parent != -1 and self.heap[i] < self.heap[parent]: 67 | self.heap[i], self.heap[parent] = self.heap[parent], self.heap[i] 68 | i = parent 69 | parent = self.parent(i) 70 | 71 | def heappop(self): 72 | """ extracts the root of the heap, min or max 73 | depending on the kind of heap""" 74 | if self.max_elements(): 75 | self.heap[0], self.heap[-1] = self.heap[-1], self.heap[0] 76 | pop = self.heap.pop() 77 | self.heapify(0) 78 | return pop 79 | raise Exception("Heap is empty") 80 | -------------------------------------------------------------------------------- /lists/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prakhar1989/Algorithms/82e64ce9d451b33c1bce64a63276d6341a1f13b0/lists/__init__.py -------------------------------------------------------------------------------- /lists/queue.py: -------------------------------------------------------------------------------- 1 | from collections import deque 2 | 3 | 4 | class Queue(object): 5 | """Wrapper around collections.deque to provide the api consistent with 6 | a Queue""" 7 | 8 | def __init__(self): 9 | self.items = deque() 10 | 11 | def __str__(self): 12 | return ("Queue of size: %d" % len(self.items)) 13 | 14 | def isEmpty(self): 15 | return len(self.items) == 0 16 | 17 | def enqueue(self, item): 18 | self.items.append(item) 19 | 20 | def dequeue(self): 21 | return self.items.popleft() 22 | 23 | def size(self): 24 | return len(self.items) 25 | -------------------------------------------------------------------------------- /lists/singlylinkedlist.py: -------------------------------------------------------------------------------- 1 | class Node(object): 2 | def __init__(self, data=None): 3 | self.data = data 4 | self.next = None 5 | 6 | def __repr__(self): 7 | return str(self.data) 8 | 9 | class SinglyLinkedList(object): 10 | def __init__(self, iterable=[]): 11 | self.head = None 12 | self.size = 0 13 | for item in iterable: self.append(item) 14 | 15 | def __repr__(self): 16 | (current, nodes) = self.head, [] 17 | while current: 18 | nodes.append(str(current)) 19 | current = current.next 20 | return "->".join(nodes) 21 | 22 | def __len__(self): 23 | return self.size 24 | 25 | def __iter__(self): 26 | current = self.head 27 | while current: 28 | yield current 29 | current = current.next 30 | raise StopIteration 31 | 32 | def __contains__(self, data): 33 | tmp = self.head 34 | found = False 35 | while tmp and not found: 36 | if data == tmp.data: 37 | found = True 38 | else: 39 | tmp = tmp.next 40 | return found 41 | 42 | def append(self, data): 43 | tmp = Node(data) 44 | tmp.next = self.head 45 | self.head = tmp 46 | self.size += 1 47 | 48 | def getHead(self): 49 | return self.head 50 | 51 | def getTail(self): 52 | tmp = self.head 53 | while tmp.next: 54 | tmp = tmp.next 55 | return tmp 56 | 57 | def delete(self, data): 58 | tmp = self.head 59 | prev = None 60 | found = False 61 | while tmp and not found: 62 | if data == tmp.data: 63 | found = True 64 | else: 65 | prev = tmp 66 | tmp = tmp.next 67 | if found: 68 | self.size -= 1 69 | if prev == None: 70 | self.head = self.head.next 71 | else: 72 | prev.next = tmp.next 73 | 74 | if __name__ == "__main__": 75 | list1 = SinglyLinkedList(range(0, 100, 10)) 76 | print list1 # testing repr 77 | print 50 in list1, 110 not in list1 # testing contains 78 | list1.delete(50) # testing delete 79 | print len(list1) == 9, 50 not in list1 # testing size 80 | -------------------------------------------------------------------------------- /lists/stack-adt.py: -------------------------------------------------------------------------------- 1 | class Stack(object): 2 | """ A simple stack ADT with top as the end of a list """ 3 | def __init__(self): 4 | self.items = [] 5 | 6 | def __str__(self): 7 | return ("Stack of size: %d" % len(self.items)) 8 | 9 | def isEmpty(self): 10 | return len(self.items) == 0 11 | 12 | def push(self, item): 13 | self.items.append(item) 14 | 15 | def pop(self): 16 | return self.items.pop() 17 | 18 | def top(self): 19 | if self.isEmpty(): return None 20 | return self.items[-1] 21 | 22 | def string_reverse(s): 23 | stack = Stack() 24 | rev = [] 25 | for c in s: stack.push(c) 26 | while not stack.isEmpty(): 27 | rev.append(stack.pop()) 28 | return "".join(rev) 29 | 30 | def match_paren(parens): 31 | """ returns true or false if parenthesis 32 | expression passed is matching""" 33 | stack = Stack() 34 | for b in parens: 35 | if b == "(": 36 | stack.push(1) 37 | else: # b == ")" 38 | if not stack.isEmpty(): 39 | stack.pop() 40 | else: 41 | return False 42 | return stack.isEmpty() 43 | 44 | def infix_to_postfix(infixexpr): 45 | prec = { '+': 2, '-': 2, '*': 3, '/': 3,'(': 1 } # denoting precedence 46 | operator_stack = Stack() 47 | operators = "+-*/()" 48 | output_list = [] 49 | for token in infixexpr.split(): 50 | if token not in operators: 51 | output_list.append(token) 52 | elif token == "(": 53 | operator_stack.push("(") 54 | elif token == ")": 55 | topToken = operator_stack.pop() 56 | while topToken != "(": 57 | output_list.append(topToken) 58 | topToken = operator_stack.pop() 59 | else: # an operator 60 | while (not operator_stack.isEmpty()) and \ 61 | (prec[operator_stack.top()] >= prec[token]): 62 | output_list.append(operator_stack.pop()) 63 | operator_stack.push(token) 64 | 65 | # tokens exhausted - empty out the stack 66 | while not operator_stack.isEmpty(): 67 | output_list.append(operator_stack.pop()) 68 | return " ".join(output_list) 69 | 70 | 71 | if __name__ == "__main__": 72 | expr = ["A * B + C * D", "( A + B ) * C - ( D - E ) * ( F + G )"] 73 | for e in expr: 74 | print infix_to_postfix(e) 75 | -------------------------------------------------------------------------------- /misc/GCD.py: -------------------------------------------------------------------------------- 1 | """ 2 | Greatest common divisor(GCD) of two integers X and Y is the largest integer that divides both X and Y. 3 | 4 | References : 5 | https://en.wikipedia.org/wiki/Euclidean_algorithm 6 | https://proofwiki.org/wiki/Euclidean_Algorithm 7 | http://stackoverflow.com/questions/6005582/how-does-the-euclidean-algorithm-work 8 | 9 | 10 | Algorithm : 11 | * If X = 0 then GCD(X,Y) = Y, as GCD(0,Y) = Y. 12 | * If Y = 0 then GCD(X,Y) = X, as GCD(X,0) = X. 13 | * Write X in quotient remainder form (X = Y * Q + R). 14 | * Find GCD(Y,R) using the Euclidean algorithm since GCD(X,Y) = GCD(Y,R). 15 | """ 16 | 17 | def greatest_common_divisor(x,y): 18 | return x if y == 0 else greatest_common_divisor(y,x%y) 19 | 20 | if __name__ == "__main__": 21 | print(greatest_common_divisor(20,25)) 22 | -------------------------------------------------------------------------------- /misc/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prakhar1989/Algorithms/82e64ce9d451b33c1bce64a63276d6341a1f13b0/misc/__init__.py -------------------------------------------------------------------------------- /misc/max_area_histogram.py: -------------------------------------------------------------------------------- 1 | """ 2 | Find the maximum rectangle (in terms of area) under a histogram in linear time. 3 | I mean the area of largest rectangle that fits entirely in the Histogram. 4 | 5 | 6 | http://tech-queries.blogspot.in/2011/03/maximum-area-rectangle-in-histogram.html 7 | http://stackoverflow.com/questions/4311694/maximize-the-rectangular-area-under-histogram 8 | http://tech-queries.blogspot.in/2011/09/find-largest-sub-matrix-with-all-1s-not.html 9 | http://stackoverflow.com/questions/3806520/finding-maximum-size-sub-matrix-of-all-1s-in-a-matrix-having-1s-and-0s 10 | 11 | """ 12 | 13 | # hist represented as ith bar has height h(i) 14 | histogram = [6, 4, 2, 1, 3, 4, 5, 2, 6] 15 | 16 | """ 17 | steps - 18 | 1. Find Li - no. of adjacent bars to the left that have h >= h(i) 19 | 2. Find Ri - no. of adjacent bars to the right that have h >= h(i) 20 | 3. Area = h(i) * (L(i) + R(i) + 1) 21 | 4. compute max area 22 | """ 23 | 24 | 25 | def find_Li(hist, i): 26 | left_edge = 0 27 | for j in range(i-1, -1, -1): 28 | if hist[j] >= hist[i]: 29 | left_edge += 1 30 | else: 31 | return left_edge 32 | 33 | return left_edge 34 | 35 | 36 | def find_Ri(hist, i): 37 | right_edge = 0 38 | for j in range(i + 1, len(hist)): 39 | if hist[j] >= hist[i]: 40 | right_edge += 1 41 | else: 42 | return right_edge 43 | 44 | return right_edge 45 | 46 | 47 | def get_area(hist, i): 48 | return hist[i] * (find_Li(hist, i) + find_Ri(hist, i) + 1) 49 | 50 | 51 | def get_max_area(hist): 52 | max_area = 0 53 | for i in range(len(hist)): 54 | area = get_area(hist, i) 55 | if area > max_area: 56 | max_area = area 57 | return max_area 58 | 59 | 60 | def max_rectangle_area(histogram): 61 | """Find the area of the largest rectangle that fits entirely under 62 | the histogram. 63 | 64 | """ 65 | stack = [] 66 | top = lambda: stack[-1] 67 | max_area = 0 68 | pos = 0 # current position in the histogram 69 | for pos, height in enumerate(histogram): 70 | start = pos # position where rectangle starts 71 | while True: 72 | if not stack or height > top()[1]: 73 | stack.append((start, height)) # push 74 | elif stack and height < top()[1]: 75 | max_area = max(max_area, top()[1] * (pos - top()[0])) 76 | start, _ = stack.pop() 77 | continue 78 | break # height == top().height goes here 79 | 80 | pos += 1 81 | for start, height in stack: 82 | max_area = max(max_area, height * (pos - start)) 83 | 84 | return max_area 85 | 86 | print max_rectangle_area(histogram) 87 | -------------------------------------------------------------------------------- /misc/modular_exponentiation.py: -------------------------------------------------------------------------------- 1 | """ 2 | Problem: https://en.wikipedia.org/wiki/Modular_exponentiation 3 | """ 4 | 5 | def modular_exponentiation(base, exp, mod): 6 | if exp < 1: 7 | raise ValueError("Exponentiation should be ve+ int") 8 | if mod == 1: 9 | return 0 10 | elif mod < 1: 11 | raise ValueError("Modulus should be ve+ int") 12 | #Initialize result to 1 13 | result = 1 14 | base %= mod 15 | while exp > 0: 16 | #multiply base to result if exp is odd 17 | if exp % 2 == 1: 18 | result = (result * base) % mod 19 | #Double base and half exp 20 | exp = exp >> 1 21 | base = (base ** 2) % mod 22 | return result -------------------------------------------------------------------------------- /misc/modular_multiplicative_inverse.py: -------------------------------------------------------------------------------- 1 | """ 2 | Problem: https://en.wikipedia.org/wiki/Modular_multiplicative_inverse 3 | """ 4 | import GCD as gcd 5 | 6 | def modular_multiplicative_inv(a, m): 7 | if m == 1: 8 | return 0 9 | if m < 1: 10 | raise ValueError('Modulus should be ve+ int > 0') 11 | # check for co-prime condition 12 | if gcd.greatest_common_divisor(a, m) != 1: 13 | raise ValueError('a and m are not co-primes') 14 | 15 | # Make var "a" positive if it's negative 16 | if a < 0: 17 | a %= m 18 | 19 | # Initialise vars 20 | m0 = m 21 | x0 = 0 22 | x1 = 1 23 | 24 | while a > 1: 25 | # Calculate quotient q; store m into temp t 26 | q = a / m 27 | t = m 28 | 29 | # Calculate m as remainder(a, m); store temp t into a 30 | m = a % m 31 | a = t 32 | 33 | # Assign x0 into temp t; Calculate x0 and store temp t into x1 34 | t = x0 35 | x0 = x1 - q * x0 36 | x1 = t 37 | 38 | # If x1 is negative then add modulus m0 39 | if x1 < 0: 40 | x1 += m0 41 | 42 | return x1 -------------------------------------------------------------------------------- /misc/rabin_miller_primality_test.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import random 3 | 4 | 5 | def is_probable_prime(n, k=7): 6 | """use Rabin-Miller algorithm to return True (n is probably prime) 7 | or False (n is definitely composite) 8 | 9 | the parameter k defines the accuracy of the test. 10 | """ 11 | if n < 6: # assuming n >= 0 in all cases... shortcut small cases here 12 | return [False, False, True, True, False, True][n] 13 | if n & 1 == 0: # should be faster than n % 2 14 | return False 15 | else: 16 | s, d = 0, n - 1 17 | while d & 1 == 0: 18 | s, d = s + 1, d >> 1 19 | # Use random.randint(2, n-2) for very large numbers 20 | for a in random.sample(xrange(2, min(n - 2, sys.maxint)), min(n - 4, k)): 21 | x = pow(a, d, n) 22 | if x != 1 and x + 1 != n: 23 | for r in xrange(1, s): 24 | x = pow(x, 2, n) 25 | if x == 1: 26 | return False # composite for sure 27 | elif x == n - 1: 28 | a = 0 # so we know loop didn't continue to end 29 | break # could be strong liar, try another a 30 | if a: 31 | return False # composite if we reached end of this loop 32 | return True # probably prime if reached end of outer loop 33 | 34 | print is_probable_prime(7) 35 | print is_probable_prime(5915587277) 36 | print is_probable_prime(48112959837082048697) 37 | print is_probable_prime(671998030559713968361666935769) 38 | print is_probable_prime(2425967623052370772757633156976982469681) 39 | print is_probable_prime(22953686867719691230002707821868552601124472329079) 40 | -------------------------------------------------------------------------------- /misc/shuffle.py: -------------------------------------------------------------------------------- 1 | """ 2 | Fisher-Yates shuffle algorithm implemented in Python. 3 | 4 | Reference : 5 | https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle 6 | http://www.geeksforgeeks.org/shuffle-a-given-array/ 7 | 8 | Algorithm: 9 | For all N indices of list, swap the element at a given index i with the element at a random index j where 0 <= j <= i. 10 | """ 11 | 12 | from random import randint 13 | 14 | def shuffle(arr): 15 | """ 16 | Shuffle a list. 17 | """ 18 | for i in range(0,len(arr)): 19 | r = randint(0,i) 20 | arr[i],arr[r] = arr[r],arr[i] 21 | 22 | if __name__ == '__main__': 23 | arr = [1,2,3,4,5,6] 24 | shuffle(arr) 25 | print(arr) 26 | -------------------------------------------------------------------------------- /misc/sieve_of_eratosthenes.py: -------------------------------------------------------------------------------- 1 | """ 2 | Implementation of Sieve of Eratosthenes algorithm to generate all the primes upto N. 3 | 4 | Algorithm : 5 | * We have a list of numbers from 1 to N. 6 | * Initially, all the numbers are marked as primes. 7 | * We go to every prime number in the list (<= N ^ 1/2) and mark all the multiples 8 | of this prime number which are bigger than the number itself as non-primes. 9 | """ 10 | 11 | from math import sqrt,ceil 12 | 13 | def generate_primes(n): 14 | bool_array = [False, False] + [True] * n # start with all values as True, except 0 and 1 15 | for i in range(2, int(ceil(sqrt(n)))): # only go to till square root of n 16 | if bool_array[i]: # if the number is marked as prime 17 | for j in range(i*i,n+1,i): # iterate through all its multiples 18 | bool_array[j] = False # and mark them as False 19 | primes = [i for i in range(n+1) if bool_array[i]] # return all numbers which are marked as True 20 | return primes 21 | -------------------------------------------------------------------------------- /sorting and basics/binary_search.py: -------------------------------------------------------------------------------- 1 | from sorting import mergesort 2 | 3 | 4 | def search(arr, item): 5 | """Performs binary search on an array 6 | with the given item and returns True or 7 | False. 8 | 9 | >>> search([5, 4, 1, 6, 2, 3, 9, 7], 2) 10 | True 11 | 12 | >>> search([5, 4, 1, 6, 2, 3, 9, 7], 8) 13 | False 14 | """ 15 | 16 | arr = mergesort(arr) 17 | 18 | first = 0 19 | last = len(arr) - 1 20 | found = False 21 | 22 | while first <= last and not found: 23 | midpoint = (first + last) // 2 24 | if arr[midpoint] == item: 25 | found = True 26 | else: 27 | if item < arr[midpoint]: 28 | last = midpoint - 1 29 | else: 30 | first = midpoint + 1 31 | 32 | return found 33 | 34 | 35 | print search([5, 4, 1, 6, 2, 3, 9, 7], 2) 36 | print search([5, 4, 1, 6, 2, 3, 9, 7], 8) 37 | -------------------------------------------------------------------------------- /sorting and basics/countinversion.py: -------------------------------------------------------------------------------- 1 | def sort_and_count(a): 2 | """ counts the number of inversions 3 | in an array and returns the count and the 4 | sorted array in O(nlogn) time 5 | 6 | >>> sort_and_count([1, 3, 5, 2, 4, 6]) 7 | ([1, 2, 3, 4, 5, 6], 3) 8 | """ 9 | 10 | if len(a) == 1: return (a, 0) 11 | (b, x) = sort_and_count(a[:(len(a)/2)]) 12 | (c, y) = sort_and_count(a[(len(a)/2):]) 13 | (d, z) = merge_and_count_inv(b, c) 14 | return (d, x + y + z) 15 | 16 | def merge_and_count_inv(b, c): 17 | d = [] 18 | count = 0 19 | i, j = 0,0 20 | while i < len(b) and j < len(c): 21 | if b[i] <= c[j]: 22 | d.append(b[i]) 23 | i += 1 24 | else: 25 | d.append(c[j]) 26 | j += 1 27 | # this works because all elements in b < c 28 | count += len(b[i:]) 29 | if b[i:]: d += b[i:] 30 | if c[j:]: d += c[j:] 31 | return d, count 32 | 33 | if __name__ == "__main__": 34 | import doctest 35 | doctest.testmod(verbose = True) 36 | -------------------------------------------------------------------------------- /sorting and basics/karatsuba.py: -------------------------------------------------------------------------------- 1 | def karatsuba(x, y, b=10): 2 | """ returns product of x, y. Uses base b 3 | in karatsuba algorithm 4 | Gives running time of O(n^1.585) as opposed to 5 | O(n^2) of naive multiplication 6 | >>> karatsuba(1234223123412323, 1234534213423333123) 7 | 1523690672850721578619752112274729L 8 | """ 9 | 10 | if x < 1000 or y < 1000: 11 | return x * y 12 | m = min(len(str(x)) / 2, len(str(y)) / 2) 13 | bm = b**m 14 | x1, x0 = x / bm, x % bm 15 | y1, y0 = y / bm, y % bm 16 | z1 = karatsuba(x1, y1, b) 17 | z3 = karatsuba(x0, y0, b) 18 | z2 = karatsuba(x1 + x0, y1 + y0, b) - z1 - z3 19 | return (bm**2)*z1 + bm*z2 + z3 20 | 21 | if __name__ == "__main__": 22 | import doctest 23 | doctest.testmod(verbose=True) 24 | -------------------------------------------------------------------------------- /sorting and basics/quicksort.py: -------------------------------------------------------------------------------- 1 | from random import randint 2 | def qsort(a, start, end): 3 | """ quicksort in O(nlogn) and no extra 4 | memory. In place implementation 5 | >>> from random import sample 6 | >>> rand_list = [sample(range(100), 10) for j in range(10)] 7 | >>> sortedresult = [sorted(r) for r in rand_list] 8 | >>> for r in rand_list: qsort(r, 0, len(r)-1) 9 | >>> result = [sortedresult[i] == rand_list[i] for i in range(len(rand_list))] 10 | >>> print sum(result) 11 | 10 12 | """ 13 | if start < end: 14 | p = choosepivot(start, end) 15 | if p != start: 16 | a[p], a[start] = a[start], a[p] 17 | equal = partition(a, start, end) 18 | qsort(a, start, equal-1) 19 | qsort(a, equal+1, end) 20 | 21 | def partition(a, l, r): 22 | """ partition array with pivot at a[0] 23 | in the array a[l...r] that returns the 24 | index of pivot element 25 | """ 26 | pivot, i = a[l], l+1 27 | for j in range(l+1, r+1): 28 | if a[j] <= pivot: 29 | a[i],a[j] = a[j],a[i] 30 | i += 1 31 | # swap pivot to its correct place 32 | a[l], a[i-1] = a[i-1], a[l] 33 | return i-1 34 | 35 | def choosepivot(s, e): 36 | return randint(s,e) 37 | 38 | if __name__ == "__main__": 39 | import doctest 40 | doctest.testmod(verbose=True) 41 | -------------------------------------------------------------------------------- /sorting and basics/scheduling.py: -------------------------------------------------------------------------------- 1 | """ data of the form -> wi, li 2 | (wi -> wieght of job i, li -> length of job i) 3 | """ 4 | test_data = """8 50 5 | 74 59 6 | 31 73 7 | 45 79 8 | 24 10 9 | 41 66 10 | 93 43 11 | 88 4 12 | 28 30 13 | 41 13 14 | 4 70 15 | 10 58 16 | 61 34 17 | 100 79 18 | 17 36 19 | 98 27 20 | 13 68 21 | 11 34 22 | """ 23 | 24 | def get_times(data): 25 | lines = [l for l in data.splitlines()] 26 | jobs = ((int(l.split()[0]), int(l.split()[1])) for l in lines) 27 | times = ((j[0]/float(j[1]), j[0], j[1]) for j in jobs) 28 | length, total_completion_time = 0,0 29 | for job in sorted(times, reverse=True): 30 | length += job[2] 31 | total_completion_time += job[1]*length 32 | print total_completion_time 33 | 34 | get_times(test_data) # ans -> 164566 35 | -------------------------------------------------------------------------------- /sorting and basics/selection_deter.py: -------------------------------------------------------------------------------- 1 | # selection problem solved deterministically 2 | from sorting import mergesort 3 | def median_of_medians(a): 4 | n = len(a) 5 | p = range(0, n, 5) + [n] 6 | sublist = [a[p[i]:p[i+1]] for i in range(len(p)-1)] 7 | mergelist = [mergesort(s)[len(s)/2] for s in sublist] 8 | # TODO: make this call recursive 9 | return mergelist[len(mergelist)/2] 10 | 11 | def select(a, i): 12 | """ returns the ith order statistic in array a 13 | in linear time.""" 14 | if i < 1 or i > len(a): 15 | return None 16 | if len(a) <= 1: return a 17 | # choose pivot 18 | p = median_of_medians(a) 19 | 20 | # partioning 21 | lesser = [x for x in a if x < p] 22 | greater = [x for x in a if x > p] 23 | j = len(lesser) 24 | 25 | if j == i: 26 | return p 27 | elif i < j: 28 | return select(lesser, i) 29 | else: # i > j 30 | return select(greater, i-j) 31 | 32 | -------------------------------------------------------------------------------- /sorting and basics/selection_random.py: -------------------------------------------------------------------------------- 1 | from random import randint 2 | def partition(a, l, r): 3 | """ partitions the array a 4 | with pivot as the first element""" 5 | pivot, i = a[l], l+1 6 | for j in range(l+1, r+1): 7 | if a[j] <= pivot: 8 | a[i], a[j] = a[j], a[i] 9 | i += 1 10 | a[i-1], a[l] = a[l], a[i-1] 11 | return i-1 12 | 13 | def random_selection(a, start, end, i): 14 | """ returns the ith order statistic 15 | in the array a in linear time 16 | >>> from random import sample 17 | >>> test_cases = [sample(range(20), 10) for i in range(10)] 18 | >>> orders = [randint(0, 9) for i in range(10)] 19 | >>> results = [sorted(test_cases[i])[orders[i]] == random_selection(test_cases[i], 0, len(test_cases[i])-1, orders[i]) for i in range(10)] 20 | >>> print sum(results) 21 | 10 22 | """ 23 | if start < end: 24 | p = choosePivot(start, end) 25 | a[start], a[p] = a[p], a[start] 26 | j = partition(a, start, end) 27 | if j == i: return a[i] 28 | if j < i: 29 | return random_selection(a, j+1, end, i) 30 | else: # j > i 31 | return random_selection(a, start, j-1, i) 32 | else: 33 | return a[start] 34 | 35 | def choosePivot(s, e): 36 | return randint(s,e) 37 | 38 | if __name__ == "__main__": 39 | from doctest import testmod 40 | testmod(verbose=True) 41 | -------------------------------------------------------------------------------- /sorting and basics/sorting.py: -------------------------------------------------------------------------------- 1 | def mergesort(arr): 2 | """ perform mergesort on a list of numbers 3 | 4 | >>> mergesort([5, 4, 1, 6, 2, 3, 9, 7]) 5 | [1, 2, 3, 4, 5, 6, 7, 9] 6 | 7 | >>> mergesort([3, 2, 4, 2, 1]) 8 | [1, 2, 2, 3, 4] 9 | """ 10 | n = len(arr) 11 | if n <= 1: return arr 12 | a1 = mergesort(arr[:n/2]) 13 | a2 = mergesort(arr[n/2:]) 14 | return merge(a1, a2) 15 | 16 | def merge(arr_a, arr_b): 17 | arr_c = [] 18 | i, j = (0, 0) 19 | while i < len(arr_a) and j < len(arr_b): 20 | if arr_a[i] <= arr_b[j]: 21 | arr_c.append(arr_a[i]) 22 | i += 1 23 | else: 24 | arr_c.append(arr_b[j]) 25 | j += 1 26 | if arr_a[i:]: arr_c.extend(arr_a[i:]) 27 | if arr_b[j:]: arr_c.extend(arr_b[j:]) 28 | return arr_c 29 | 30 | def quicksort(a): 31 | """ quicksort implementation in python 32 | NOTE: This algo uses O(n) extra space 33 | to compute quicksort. 34 | 35 | >>> quicksort([6, 4, 8, 2, 1, 9, 10]) 36 | [1, 2, 4, 6, 8, 9, 10] 37 | """ 38 | n = len(a) 39 | if n<=1: 40 | return a 41 | else: 42 | from random import randrange 43 | pivot = a.pop(randrange(n)) 44 | lesser = quicksort([x for x in a if x < pivot]) 45 | greater = quicksort([x for x in a if x >= pivot]) 46 | return lesser + [pivot] + greater 47 | 48 | 49 | def selectionsort(a): 50 | """ selectionsort implementation 51 | 52 | >>> selectionsort([6, 4, 8, 2, 1, 9, 10]) 53 | [1, 2, 4, 6, 8, 9, 10] 54 | """ 55 | for i in range(len(a)): 56 | min = i 57 | for j in range(i,len(a)): 58 | if a[j] < a[min]: 59 | min = j 60 | a[i],a[min] = a[min], a[i] 61 | return a 62 | 63 | def bubblesort(a): 64 | """ bubble sort implementation 65 | 66 | >>> bubblesort([6, 4, 8, 2, 1, 9, 10]) 67 | [1, 2, 4, 6, 8, 9, 10] 68 | """ 69 | for i in range(len(a)): 70 | for j in range(i, len(a)): 71 | if a[i] > a[j]: 72 | a[i], a[j] = a[j], a[i] 73 | return a 74 | 75 | 76 | def insertionsort(a): 77 | """ insertion sort implementation 78 | >>> insertionsort([6, 4, 8, 2, 1, 9, 10]) 79 | [1, 2, 4, 6, 8, 9, 10] 80 | """ 81 | for i in range(len(a)): 82 | item = a[i] 83 | j = i 84 | while j > 0 and a[j-1] > item: 85 | a[j] = a[j-1] 86 | j -= 1 87 | a[j] = item 88 | return a 89 | 90 | if __name__ == "__main__": 91 | import doctest 92 | doctest.testmod(verbose=True) 93 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prakhar1989/Algorithms/82e64ce9d451b33c1bce64a63276d6341a1f13b0/tests/__init__.py -------------------------------------------------------------------------------- /tests/assign.py: -------------------------------------------------------------------------------- 1 | import os, sys 2 | import operator 3 | sys.path.append(os.path.join(os.getcwd(), os.path.pardir)) 4 | from graphs.graph import graph 5 | from itertools import * 6 | from union_find.unionfind import UnionFind 7 | 8 | def ham_dist(e1, e2): 9 | """ computes hamming distance between two strings e1 and e2 """ 10 | ne = operator.ne 11 | return sum(imap(ne, e1, e2)) 12 | 13 | path = "clustering3.txt" 14 | nodes = open(path).readlines() 15 | uf = UnionFind() 16 | 17 | # bitcount = {i: nodes[i].count('1') for i in range(len(nodes))} 18 | # similar = [(bitcount[i]-9, ham_dist(nodes[i], nodes[0])) for i in range(1, len(nodes))] 19 | # print nodes[1].count('1') - nodes[2].count('1') 20 | # print hamdist(nodes[1], nodes[2]) 21 | for i in range(len(nodes)): 22 | for j in range(i+1, len(nodes)): 23 | print i, j, ham_dist(nodes[i], nodes[j]) 24 | -------------------------------------------------------------------------------- /tests/digraph_test.py: -------------------------------------------------------------------------------- 1 | import os, sys 2 | sys.path.append(os.path.join(os.getcwd(), os.path.pardir)) 3 | import unittest 4 | from graphs.digraph import digraph 5 | 6 | class test_graph(unittest.TestCase): 7 | 8 | def setUp(self): 9 | self.gr = digraph() 10 | self.gr.add_nodes(["a", "b", "c", "d", "e", "f"]) 11 | self.gr.add_edges([("a","b"), ("b", "c"), ("a", "d"), ("d", "e"), ("d", "f")]) 12 | self.gr.add_edge(("f", "b")) 13 | 14 | def test_nodes_method(self): 15 | self.assertEqual(len(self.gr.nodes()), 6) 16 | 17 | def test_add_node_method(self): 18 | self.gr.add_node("g") 19 | self.assertEqual(len(self.gr.nodes()), 7) 20 | 21 | def test_has_node_method(self): 22 | self.assertTrue(self.gr.has_node("a")) 23 | 24 | def test_neighbors_method(self): 25 | self.assertEqual(len(self.gr.neighbors("a")), 2) 26 | 27 | def test_del_node_method(self): 28 | self.gr.del_node("a") 29 | self.assertFalse(self.gr.has_node("a")) 30 | self.assertEqual(len(self.gr.edges()), 4) 31 | 32 | def test_has_edge_method(self): 33 | self.assertTrue(self.gr.has_edge(("a", "b"))) 34 | self.assertFalse(self.gr.has_edge(("b", "a"))) 35 | 36 | def test_add_duplicate_node_method_throws_exception(self): 37 | self.assertRaises(Exception, self.gr.add_node, "a") 38 | 39 | def test_del_nonexistent_node_throws_exception(self): 40 | self.assertRaises(Exception, self.gr.del_node, "z") 41 | 42 | def test_add_duplicate_edge_throws_exception(self): 43 | self.assertRaises(Exception, self.gr.add_edge, ("a", "b")) 44 | 45 | def test_adding_self_loop(self): 46 | self.gr.add_edge(("a", "a")) 47 | self.assertTrue(self.gr.has_edge(("a", "a"))) 48 | 49 | def test_remove_self_loop(self): 50 | self.gr.add_edge(("a", "a")) 51 | self.gr.del_edge(("a", "a")) 52 | self.assertFalse(self.gr.has_edge(("a", "a"))) 53 | 54 | def test_edges_method(self): 55 | self.assertEqual(len(self.gr.edges()), 6) 56 | 57 | def test_node_orders_method(self): 58 | self.assertEqual(self.gr.node_order("d"), 2) 59 | 60 | def test_del_edge_method(self): 61 | self.gr.del_edge(("b", "c")) 62 | self.assertFalse(self.gr.has_edge(("b", "c"))) 63 | 64 | def test_deleting_non_existing_edge_raises_exception(self): 65 | self.assertRaises(Exception, self.gr.del_edge, ("a", "z")) 66 | 67 | def test_get_default_weight(self): 68 | self.assertEqual(self.gr.get_edge_weight(("a", "b")), 1) 69 | 70 | def test_set_weight_on_existing_edge(self): 71 | self.gr.set_edge_weight(("a", "b"), 10) 72 | self.assertEqual(self.gr.get_edge_weight(("a", "b")), 10) 73 | 74 | def test_weight_for_nonexisting_edge(self): 75 | self.assertRaises(Exception, self.gr.get_edge_weight, ("a", "c")) 76 | 77 | def test_get_transpose_method(self): 78 | transpose = self.gr.get_transpose() 79 | self.assertEqual(len(transpose.nodes()), len(self.gr.nodes())) 80 | self.assertEqual(len(transpose.edges()), len(self.gr.edges())) 81 | self.assertTrue(self.gr.has_edge(("a", "b"))) 82 | self.assertTrue(transpose.has_edge(("b", "a"))) 83 | self.assertFalse(transpose.has_edge(("a", "b"))) 84 | 85 | if __name__ == "__main__": 86 | unittest.main() 87 | -------------------------------------------------------------------------------- /tests/gcd_test.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import fractions 3 | import GCD 4 | 5 | class TestEuclideanGCD(unittest.TestCase): 6 | def test_gcd(self): 7 | self.assertEqual(fractions.gcd(30,50),GCD.greatest_common_divisor(30,50)) 8 | self.assertEqual(fractions.gcd(55555,123450),GCD.greatest_common_divisor(55555,123450)) 9 | self.assertEqual(fractions.gcd(-30,-50),GCD.greatest_common_divisor(-30,-50)) 10 | self.assertEqual(fractions.gcd(-1234,1234),GCD.greatest_common_divisor(-1234,1234)) 11 | 12 | if __name__ == "__main__": 13 | unittest.main() 14 | -------------------------------------------------------------------------------- /tests/graph_algorithms_test.py: -------------------------------------------------------------------------------- 1 | import os, sys 2 | sys.path.append(os.path.join(os.getcwd(), os.path.pardir)) 3 | import unittest 4 | from graphs.digraph import digraph 5 | from graphs.graph import graph 6 | from graphs.graph_algorithms import * 7 | 8 | class test_graph(unittest.TestCase): 9 | 10 | def setUp(self): 11 | self.gr = graph() 12 | self.gr.add_nodes(["s", "a", "b", "c", "d", "e", "f", "g", "h", "j", "k", "l"]) 13 | self.gr.add_edges([("s", "a"), ("s", "b"), ("a", "c"), ("c", "e")]) 14 | self.gr.add_edges([("e", "d"), ("d", "b"), ("a", "b"), ("c", "d")]) 15 | self.gr.add_edges([("g", "h"), ("f", "g")]) 16 | self.gr.add_edges([("j", "k"), ("j", "l")]) 17 | 18 | self.digr = digraph() 19 | self.digr.add_nodes(['s', 'a', 'b', 'c', 'd', 'e', 'f']) 20 | self.digr.add_edges([("s", "a"), ("a", "b"), ("b", "a"), ("c", "b")]) 21 | self.digr.add_edges([("b", "s"), ("s", "d"), ("d", "e"), ("e", "d")]) 22 | self.digr.add_edges([("b", "f"), ("e", "f")]) 23 | 24 | def test_bfs_undirected_graph(self): 25 | self.assertEqual(len(BFS(self.gr, "s")), 6) 26 | self.assertEqual(len(BFS(self.gr, "j")), 3) 27 | self.assertEqual(len(BFS(self.gr, "g")), 3) 28 | 29 | def test_bfs_directed_graph(self): 30 | self.assertEqual(len(BFS(self.digr, "s")), 6) 31 | self.assertEqual(len(BFS(self.digr, "c")), 7) 32 | self.assertEqual(len(BFS(self.digr, "f")), 1) 33 | 34 | def test_dfs_undirected_graph(self): 35 | self.assertEqual(len(DFS(self.gr, "s")), 6) 36 | self.assertEqual(len(DFS(self.gr, "j")), 3) 37 | self.assertEqual(len(DFS(self.gr, "g")), 3) 38 | 39 | def test_dfs_directed_graph(self): 40 | self.assertEqual(len(DFS(self.digr, "s")), 6) 41 | self.assertEqual(len(DFS(self.digr, "c")), 7) 42 | self.assertEqual(len(DFS(self.digr, "f")), 1) 43 | 44 | def test_shortest_hops_undirected_graph(self): 45 | self.assertEqual(shortest_hops(self.gr, "s")["c"], 2) 46 | self.assertEqual(shortest_hops(self.gr, "c")["s"], 2) 47 | self.assertEqual(shortest_hops(self.gr, "s")["s"], 0) 48 | self.assertEqual(shortest_hops(self.gr, "c")["j"], float('inf')) 49 | 50 | def test_shortest_hops_directed_graph(self): 51 | self.assertEqual(shortest_hops(self.digr, "s")["f"], 3) 52 | self.assertEqual(shortest_hops(self.digr, "f")["s"], float('inf')) 53 | self.assertEqual(shortest_hops(self.digr, "s")["s"], 0) 54 | self.assertEqual(shortest_hops(self.digr, "s")["c"], float('inf')) 55 | 56 | def test_undirected_connected_component(self): 57 | self.assertEqual(len(undirected_connected_components(self.gr)), 3) 58 | self.assertRaises(Exception, undirected_connected_components, self.digr) 59 | 60 | def test_topological_ordering(self): 61 | dag = digraph() # directed acyclic graph 62 | dag.add_nodes(["a", "b", "c", "d", "e", "f", "g", "h"]) 63 | dag.add_edges([("a", "b"), ("a", "c"), ("a", "e"), ("d", "a")]) 64 | dag.add_edges([("g", "b"), ("g", "f"), ("f", "e"), ("h", "f"), ("h", "a")]) 65 | order = {o[0]: o[1] for o in topological_ordering(dag)} 66 | self.assertEqual(sum([order[u] < order[v] for (u, v) in 67 | dag.edges()]), len(dag.edges())) # all comparisons are True 68 | 69 | def test_directed_connected_components(self): 70 | digr = digraph() 71 | digr.add_nodes(["a", "b", "c", "d", "e", "f", "g", "h", "i"]) 72 | digr.add_edges([("b", "a"), ("a", "c"), ("c", "b"), ("d", "b")]) 73 | digr.add_edges([("d", "f"), ("f", "e"), ("e", "d"), ("g", "e")]) 74 | digr.add_edges([("g", "h"), ("h", "i"), ("i", "g")]) 75 | self.assertEqual(len(directed_connected_components(digr)), 3) 76 | digr2 = digraph() 77 | digr2.add_nodes(["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k"]) 78 | digr2.add_edges([("a", "b"), ("b", "c"), ("c", "a"), ("b", "d"), ("d", "e")]) 79 | digr2.add_edges([("e", "f"), ("f", "g"), ("g", "e"), ("d", "g"), ("i", "f")]) 80 | digr2.add_edges([("h", "g"), ("c", "h"), ("c", "k"), ("h", "i"), ("i", "j")]) 81 | digr2.add_edges([("h", "j"), ("j", "k"), ("k", "h")]) 82 | self.assertEqual(len(directed_connected_components(digr2)), 4) 83 | 84 | def test_shortest_path_in_directed_graph(self): 85 | digr = digraph() 86 | digr.add_nodes(["a", "b", "c", "d", "e", "f"]) 87 | digr.add_edge(("a", "b"), 7) 88 | digr.add_edge(("a", "c"), 9) 89 | digr.add_edge(("a", "f"), 14) 90 | digr.add_edge(("f", "e"), 9) 91 | digr.add_edge(("c", "f"), 2) 92 | digr.add_edge(("c", "d"), 11) 93 | digr.add_edge(("b", "c"), 10) 94 | digr.add_edge(("b", "d"), 15) 95 | digr.add_edge(("d", "e"), 6) 96 | self.assertEqual(shortest_path(digr, "a")["a"], 0) 97 | self.assertEqual(shortest_path(digr, "a")["b"], 7) 98 | self.assertEqual(shortest_path(digr, "a")["c"], 9) 99 | self.assertEqual(shortest_path(digr, "a")["d"], 20) 100 | self.assertEqual(shortest_path(digr, "a")["e"], 20) 101 | self.assertEqual(shortest_path(digr, "a")["f"], 11) 102 | 103 | def test_prims_minimum_spanning_tree(self): 104 | gr = graph() 105 | gr.add_nodes(["a", "b", "c", "d"]) 106 | gr.add_edge(("a", "b"), 4) 107 | gr.add_edge(("b", "c"), 3) 108 | gr.add_edge(("a", "c"), 1) 109 | gr.add_edge(("c", "d"), 2) 110 | min_cost = minimum_spanning_tree(gr) 111 | self.assertEqual(min_cost, 6) 112 | 113 | def test_kruskals_minimum_spanning_tree(self): 114 | gr = graph() 115 | gr.add_nodes(["a", "b", "c", "d"]) 116 | gr.add_edge(("a", "b"), 4) 117 | gr.add_edge(("b", "c"), 3) 118 | gr.add_edge(("a", "c"), 1) 119 | gr.add_edge(("c", "d"), 2) 120 | min_cost = kruskal_MST(gr) 121 | self.assertEqual(min_cost, 6) 122 | 123 | if __name__ == "__main__": 124 | unittest.main() 125 | -------------------------------------------------------------------------------- /tests/graph_test.py: -------------------------------------------------------------------------------- 1 | import os, sys 2 | sys.path.append(os.path.join(os.getcwd(), os.path.pardir)) 3 | import unittest 4 | from graphs.graph import graph 5 | 6 | 7 | class test_graph(unittest.TestCase): 8 | 9 | def setUp(self): 10 | self.gr = graph() 11 | self.gr.add_nodes(["a", "b", "c", "d", "e", "f"]) 12 | self.gr.add_edge(("a","b")) 13 | self.gr.add_edge(("a","f")) 14 | self.gr.add_edge(("b","c")) 15 | self.gr.add_edge(("c","e")) 16 | self.gr.add_edge(("c","d")) 17 | self.gr.add_edge(("d","f")) 18 | 19 | def test_nodes_method(self): 20 | self.assertEqual(len(self.gr.nodes()), 6) 21 | 22 | def test_add_node_method(self): 23 | self.gr.add_node("g") 24 | self.assertEqual(len(self.gr.nodes()), 7) 25 | 26 | def test_has_node_method(self): 27 | self.assertTrue(self.gr.has_node("a")) 28 | 29 | def test_neighbors_method(self): 30 | self.assertEqual(len(self.gr.neighbors("a")), 2) 31 | 32 | def test_del_node_method(self): 33 | self.gr.del_node("a") 34 | self.assertFalse(self.gr.has_node("a")) 35 | self.assertEqual(len(self.gr.edges()), 8) 36 | 37 | def test_has_edge_method(self): 38 | self.assertTrue(self.gr.has_edge(("a", "b"))) 39 | self.assertFalse(self.gr.has_edge(("a", "d"))) 40 | 41 | def test_add_duplicate_node_method_throws_exception(self): 42 | self.assertRaises(Exception, self.gr.add_node, "a") 43 | 44 | def test_del_nonexistent_node_throws_exception(self): 45 | self.assertRaises(Exception, self.gr.del_node, "z") 46 | 47 | def test_add_duplicate_edge_throws_exception(self): 48 | self.assertRaises(Exception, self.gr.add_edge, ("a", "b")) 49 | 50 | def test_add_edge_from_non_existing_node(self): 51 | self.assertRaises(Exception, self.gr.add_edge, ("b", "z")) 52 | 53 | def test_adding_self_loop(self): 54 | self.gr.add_edge(("a", "a")) 55 | self.assertTrue(self.gr.has_edge(("a", "a"))) 56 | 57 | def test_remove_self_loop(self): 58 | self.gr.add_edge(("a", "a")) 59 | self.gr.del_edge(("a", "a")) 60 | self.assertFalse(self.gr.has_edge(("a", "a"))) 61 | 62 | def test_edges_method(self): 63 | self.assertEqual(len(self.gr.edges()), 2*6) 64 | 65 | def test_add_edges_method(self): 66 | self.gr.add_edges([("a", "c"), ("c", "f"), ("d", "e")]) 67 | self.assertTrue(self.gr.has_edge(("a", "c"))) 68 | self.assertTrue(self.gr.has_edge(("c", "f"))) 69 | self.assertTrue(self.gr.has_edge(("d", "e"))) 70 | 71 | def test_node_orders_method(self): 72 | self.assertEqual(self.gr.node_order("c"), 3) 73 | 74 | def test_del_edge_method(self): 75 | self.gr.del_edge(("a", "f")) 76 | self.assertFalse(self.gr.has_edge(("a", "f"))) 77 | 78 | def test_deleting_non_existing_edge_raises_exception(self): 79 | self.assertRaises(Exception, self.gr.del_edge, ("a", "z")) 80 | 81 | def test_get_default_weight(self): 82 | self.assertEqual(self.gr.get_edge_weight(("a", "b")), 1) 83 | 84 | def test_set_weight_on_existing_edge(self): 85 | self.gr.set_edge_weight(("a", "b"), 10) 86 | self.assertEqual(self.gr.get_edge_weight(("a", "b")), 10) 87 | 88 | def test_weight_for_nonexisting_edge(self): 89 | self.assertRaises(Exception, self.gr.get_edge_weight, ("a", "c")) 90 | 91 | if __name__ == "__main__": 92 | unittest.main() 93 | -------------------------------------------------------------------------------- /tests/heap_test.py: -------------------------------------------------------------------------------- 1 | import os, sys 2 | sys.path.append(os.path.join(os.getcwd(), os.path.pardir)) 3 | import unittest 4 | from heaps.minheap import minheap 5 | from heaps.maxheap import maxheap 6 | import random 7 | 8 | class test_heap(unittest.TestCase): 9 | 10 | def setUp(self): 11 | self.h = minheap() 12 | self.m = maxheap() 13 | self.a = [random.choice(range(50)) for i in range(10)] 14 | self.h.build_heap(self.a) 15 | self.m.build_heap(self.a) 16 | 17 | def test_heap_pop(self): 18 | self.assertEqual(min(self.a), self.h.heappop()) 19 | self.assertEqual(max(self.a), self.m.heappop()) 20 | 21 | def test_max_elements(self): 22 | self.assertEqual(len(self.a), self.h.max_elements()) 23 | self.assertEqual(len(self.a), self.m.max_elements()) 24 | 25 | def test_heap_sort(self): 26 | sorted_h = [self.h.heappop() for i in range(self.h.max_elements())] 27 | sorted_m = [self.m.heappop() for i in range(self.m.max_elements())] 28 | self.assertEqual(sorted_h, sorted(self.a)) 29 | self.assertEqual(sorted_m, sorted(self.a, reverse=True)) 30 | 31 | def test_heap_push_method(self): 32 | self.h.heappush(-1) 33 | self.assertEqual(-1, self.h.heappop()) 34 | self.m.heappush(100) 35 | self.assertEqual(100, self.m.heappop()) 36 | 37 | if __name__ == "__main__": 38 | unittest.main() 39 | -------------------------------------------------------------------------------- /tests/lcs_test.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import lcs 3 | 4 | class TestLCS(unittest.TestCase): 5 | def test_lcs(self): 6 | self.assertEqual(lcs.longest_common_subsequence("ABCD", "BBDABXYDCCAD"), (4, "ABCD")) 7 | self.assertEqual(lcs.longest_common_subsequence("BANANA", "ATANA"), (4, "AANA")) 8 | self.assertEqual(lcs.longest_common_subsequence("ABCDEFG", "BDGK"), (3, "BDG")) 9 | 10 | if __name__ == "__main__": 11 | unittest.main() 12 | -------------------------------------------------------------------------------- /tests/modular_exponentiation_test.py: -------------------------------------------------------------------------------- 1 | import os, sys 2 | import unittest 3 | sys.path.append(os.path.join(os.getcwd(), os.path.pardir)) 4 | from misc import modular_exponentiation as me 5 | 6 | class TestLCS(unittest.TestCase): 7 | def test_modular_exponentiation(self): 8 | self.assertEqual(me.modular_exponentiation(2, 10, 100), 24) 9 | self.assertEqual(me.modular_exponentiation(2, 200, 10), 6) 10 | self.assertEqual(me.modular_exponentiation(5, 20, 1), 0) 11 | #self.assertEqual(me.modular_exponentiation(8, 1, 10), 8) 12 | self.assertRaises(ValueError, me.modular_exponentiation, 12, -1, 10) 13 | self.assertRaises(ValueError, me.modular_exponentiation, 12, 5, 0) 14 | 15 | if __name__ == "__main__": 16 | unittest.main() -------------------------------------------------------------------------------- /tests/modular_multiplicative_inverse_test.py: -------------------------------------------------------------------------------- 1 | import os, sys 2 | import unittest 3 | sys.path.append(os.path.join(os.getcwd(), os.path.pardir)) 4 | from misc import modular_multiplicative_inverse as mmi 5 | 6 | class TestLCS(unittest.TestCase): 7 | def test_modular_multiplicative_inverse(self): 8 | self.assertEqual(mmi.modular_multiplicative_inv(10, 7), 5) 9 | self.assertEqual(mmi.modular_multiplicative_inv(45, 13), 11) 10 | self.assertEqual(mmi.modular_multiplicative_inv(52, 1), 0) 11 | 12 | self.assertRaises(ValueError, mmi.modular_multiplicative_inv, 12, -1) 13 | self.assertRaises(ValueError, mmi.modular_multiplicative_inv, 12, 2) 14 | 15 | if __name__ == "__main__": 16 | unittest.main() -------------------------------------------------------------------------------- /tests/sieve_test.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from sieve_of_eratosthenes import calculate_primes 3 | 4 | class TestSieveOfEratosthenes(unittest.TestCase): 5 | def test_primes(self): 6 | self.prime_list = [2,3,5,7,11,13,17,19] 7 | self.assertEqual(self.prime_list,calculate_primes(20)) 8 | 9 | if __name__ == '__main__': 10 | unittest.main() 11 | -------------------------------------------------------------------------------- /tests/singly_linked_list_test.py: -------------------------------------------------------------------------------- 1 | import os, sys 2 | sys.path.append(os.path.join(os.getcwd(), os.path.pardir)) 3 | import unittest 4 | from lists.singlylinkedlist import SinglyLinkedList 5 | 6 | class test_graph(unittest.TestCase): 7 | def setUp(self): 8 | self.tens = SinglyLinkedList(range(0, 100, 10)) 9 | self.blankList = SinglyLinkedList() 10 | 11 | def test_length_method(self): 12 | self.assertEqual(len(self.tens), 10) 13 | self.assertEqual(len(self.blankList), 0) 14 | 15 | def test_add_method(self): 16 | self.blankList.append(50) 17 | self.tens.append(110) 18 | self.assertEqual(len(self.blankList), 1) 19 | self.assertEqual(len(self.tens), 11) 20 | 21 | if __name__ == "__main__": 22 | unittest.main() 23 | -------------------------------------------------------------------------------- /tests/unionfind_test.py: -------------------------------------------------------------------------------- 1 | import os, sys 2 | sys.path.append(os.path.join(os.getcwd(), os.path.pardir)) 3 | import unittest 4 | from union_find.unionfind import UnionFind 5 | 6 | class test_unionfind(unittest.TestCase): 7 | 8 | def setUp(self): 9 | self.uf = UnionFind() 10 | self.uf.insert("a", "b") 11 | self.uf.insert("b", "c") 12 | self.uf.insert("i", "j") 13 | 14 | def test_get_parent_method(self): 15 | self.assertEqual("a", self.uf.get_leader("a")) 16 | self.assertEqual("a", self.uf.get_leader("b")) 17 | self.assertEqual("a", self.uf.get_leader("c")) 18 | self.assertEqual("i", self.uf.get_leader("j")) 19 | self.assertEqual("i", self.uf.get_leader("i")) 20 | self.assertNotEqual(self.uf.get_leader("a"), self.uf.get_leader("i")) 21 | 22 | def test_insert_method(self): 23 | self.uf.insert("c", "d") 24 | self.assertEqual(self.uf.get_leader("c"), self.uf.get_leader("d")) 25 | self.assertEqual(self.uf.get_leader("a"), self.uf.get_leader("d")) 26 | 27 | def test_insert_one_node(self): 28 | self.uf.insert('z') 29 | self.assertEqual(self.uf.get_leader('z'), 'z') 30 | self.assertEqual(self.uf.count_groups(), 3) 31 | 32 | def test_make_union_method(self): 33 | self.uf.make_union(self.uf.get_leader("a"), self.uf.get_leader("i")) 34 | self.assertEqual(self.uf.get_leader("a"), self.uf.get_leader("i")) 35 | 36 | def test_make_union_with_invalid_leader_raises_exception(self): 37 | self.assertRaises(Exception, self.uf.make_union, "a", "z") 38 | 39 | def test_get_count(self): 40 | self.uf.insert("z", "y") 41 | self.assertEqual(self.uf.count_groups(), 3) 42 | 43 | if __name__ == "__main__": 44 | unittest.main() 45 | -------------------------------------------------------------------------------- /trees/binarysearchtree.py: -------------------------------------------------------------------------------- 1 | """ Binary Search Tree 2 | Methods - find_element(value), get_max(), get_min(), successor(value), 3 | insert, delete, values() """ 4 | 5 | class Node(object): 6 | def __init__(self, value): 7 | self.right = None 8 | self.left = None 9 | self.parent = None 10 | self.value = value 11 | 12 | def __repr__(self): 13 | return "Node with value - %s" % self.value 14 | 15 | class BinarySearchTree(object): 16 | def __init__(self): 17 | self.root = None 18 | self.len = 0 19 | 20 | def __len__(self): 21 | return self.len 22 | 23 | def is_empty(self): 24 | return self.root == None 25 | 26 | def _inorder(self, node, values): 27 | if node != None: 28 | self._inorder(node.left, values) 29 | values.append(node.value) 30 | self._inorder(node.right, values) 31 | 32 | def _preorder(self, node, values): 33 | if node != None: 34 | values.append(node.value) 35 | self._preorder(node.left, values) 36 | self._preorder(node.right, values) 37 | 38 | def _postorder(self, node, values): 39 | if node != None: 40 | self._postorder(node.left, values) 41 | self._postorder(node.right, values) 42 | values.append(node.value) 43 | 44 | def values(self, reverse = False, order="in"): 45 | values = [] 46 | if order == "in": 47 | self._inorder(self.root, values) 48 | elif order == "pre": 49 | self._preorder(self.root, values) 50 | else: #order is post 51 | self._postorder(self.root, values) 52 | if reverse: 53 | return values[::-1] 54 | return values 55 | 56 | def _search(self, root, value): 57 | if not root or root.value == value: 58 | return root 59 | if value < root.value: 60 | return self._search(root.left, value) 61 | else: 62 | return self._search(root.right, value) 63 | 64 | def find_element(self, value): 65 | return self._search(self.root, value) 66 | 67 | def _extremes(self, root, find_min = True): 68 | while (find_min and root.left) or (not find_min and root.right): 69 | if find_min: 70 | root = root.left 71 | else: # find max 72 | root = root.right 73 | return root 74 | 75 | def get_min(self): 76 | """ returns the element with the minimum value """ 77 | return self._extremes(self.root, find_min=True) 78 | 79 | def get_max(self): 80 | """ returns the element with the maximum value """ 81 | return self._extremes(self.root, find_min=False) 82 | 83 | def successor(self, value): 84 | """ returns the successor of the element with value - value""" 85 | node = self.find_element(value) 86 | if not node: 87 | return None 88 | if node.right: 89 | return self._extremes(node.right, find_min=True) 90 | parent = node.parent 91 | while parent and parent.right == node: 92 | node = parent 93 | parent = parent.parent 94 | return parent 95 | 96 | def insert(self, value): 97 | new_node = Node(value) 98 | if self.is_empty(): 99 | self.root = new_node 100 | else: 101 | node = self.root 102 | while node and node.value != value: 103 | if node.value == value: 104 | return 105 | parent = node 106 | if node.value < value: 107 | node = node.right 108 | else: 109 | node = node.left 110 | if parent.value > value: 111 | parent.left = new_node 112 | else: 113 | parent.right = new_node 114 | new_node.parent = parent 115 | self.len += 1 116 | return 117 | 118 | def delete(self, value): 119 | """ deletes a node from tree with value - value """ 120 | node = self.find_element(value) 121 | if not node: 122 | return None 123 | if not node.left or not node.right: 124 | node_spliced = node 125 | else: 126 | node_spliced = self.successor(node.value) 127 | if node_spliced.left: 128 | temp_node = node_spliced.left 129 | else: 130 | temp_node = node_spliced.right 131 | if temp_node: 132 | temp_node.parent = node_spliced.parent 133 | if not node_spliced.parent: 134 | self.root = temp_node 135 | elif node_spliced == node_spliced.parent.left: 136 | node_spliced.parent.left = temp_node 137 | else: 138 | node_spliced.parent.right = temp_node 139 | 140 | if node != node_spliced: 141 | node.value = node_spliced.value 142 | return node_spliced 143 | -------------------------------------------------------------------------------- /trees/trie.py: -------------------------------------------------------------------------------- 1 | """ Tries in python 2 | Methods - insert_key(k, v) 3 | has_key(k) 4 | retrie_val(k) 5 | start_with_prefix(prefix) 6 | """ 7 | 8 | 9 | def _get_child_branches(trie): 10 | """ 11 | Helper method for getting branches 12 | """ 13 | return trie[1:] 14 | 15 | 16 | def _get_child_branch(trie, c): 17 | """ 18 | Get branch matching the character 19 | """ 20 | for branch in _get_child_branches(trie): 21 | if branch[0] == c: 22 | return branch 23 | 24 | return None 25 | 26 | 27 | def _retrive_branch(k, trie): 28 | """ 29 | Get branch matching the key word 30 | """ 31 | if not k: 32 | return None 33 | 34 | for c in k: 35 | child_branch = _get_child_branch(trie, c) 36 | if not child_branch: 37 | return None 38 | trie = child_branch 39 | 40 | return trie 41 | 42 | 43 | def _is_trie_bucket(bucket): 44 | if len(bucket) != 2: 45 | return False 46 | 47 | return type(bucket[1]) is tuple 48 | 49 | 50 | def _get_bucket_key(bucket): 51 | if not _is_trie_bucket(bucket): 52 | return None 53 | 54 | return bucket[1][0] 55 | 56 | 57 | def has_key(k, trie): 58 | """ 59 | Check if trie contain the key word 60 | """ 61 | return _retrive_branch(k, trie) is not None 62 | 63 | 64 | def retrie_val(k, trie): 65 | key_tuple = _retrive_branch(k, trie) 66 | if not key_tuple: 67 | return None 68 | 69 | return key_tuple[1] 70 | 71 | 72 | def insert_key(key, v, trie): 73 | """ 74 | Insert a (key, value) pair into trie 75 | """ 76 | if not key or has_key(key, trie): 77 | return 78 | 79 | for char in key: 80 | branch = _get_child_branch(trie, char) 81 | if not branch: 82 | new_branch = [char] 83 | trie.append(new_branch) 84 | trie = new_branch 85 | else: 86 | trie = branch 87 | trie.append((key, v)) 88 | 89 | 90 | def start_with_prefix(prefix, trie): 91 | """ 92 | Find words start with prefix 93 | """ 94 | branch = _retrive_branch(prefix, trie) 95 | if not branch: 96 | return [] 97 | 98 | prefix_list = [] 99 | q = branch[1:] 100 | while q: 101 | curr_branch = q.pop(0) 102 | if _is_trie_bucket(curr_branch): 103 | prefix_list.append(_get_bucket_key(curr_branch)) 104 | else: 105 | q.extend(curr_branch[1:]) 106 | 107 | return prefix_list 108 | 109 | if __name__ == "__main__": 110 | trie = [[]] 111 | states = """ 112 | Alabama 113 | Alaska 114 | Arizona 115 | Arkansas 116 | California 117 | Colorado 118 | Connecticut 119 | Delaware 120 | Florida 121 | Georgia 122 | Hawaii 123 | Idaho 124 | Illinois 125 | Indiana 126 | Iowa 127 | Kansas 128 | Kentucky 129 | Louisiana 130 | Maine 131 | Maryland 132 | Massachusetts 133 | Michigan 134 | Minnesota 135 | Mississippi 136 | Missouri 137 | Montana 138 | Nebraska 139 | Nevada 140 | New Hampshire 141 | New Jersey 142 | New Mexico 143 | New York 144 | North Carolina 145 | North Dakota 146 | Ohio 147 | Oklahoma 148 | Oregon 149 | Pennsylvania 150 | Rhode Island 151 | South Carolina 152 | South Dakota 153 | Tennessee 154 | Texas 155 | Utah 156 | Vermont 157 | Virginia 158 | Washington 159 | West Virginia 160 | Wisconsin 161 | Wyoming""" 162 | states_list = [w.strip().lower() for w in states.splitlines() if w] 163 | for state in states_list: 164 | insert_key(state, True, trie) 165 | print start_with_prefix("new", trie) 166 | -------------------------------------------------------------------------------- /union_find/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prakhar1989/Algorithms/82e64ce9d451b33c1bce64a63276d6341a1f13b0/union_find/__init__.py -------------------------------------------------------------------------------- /union_find/unionfind.py: -------------------------------------------------------------------------------- 1 | class UnionFind(object): 2 | """ Disjoint Set data structure supporting union and find operations used 3 | for Kruskal's MST algorithm 4 | Methods - 5 | insert(a, b) -> inserts 2 items in the sets 6 | get_leader(a) -> returns the leader(representative) corresponding to item a 7 | make_union(leadera, leaderb) -> unions two sets with leadera and leaderb 8 | in O(nlogn) time where n the number of elements in the data structure 9 | count_keys() -> returns the number of groups in the data structure 10 | """ 11 | 12 | def __init__(self): 13 | self.leader = {} 14 | self.group = {} 15 | self.__repr__ = self.__str__ 16 | 17 | def __str__(self): 18 | return str(self.group) 19 | 20 | def get_sets(self): 21 | """ returns a list of all the sets in the data structure""" 22 | return [i[1] for i in self.group.items()] 23 | 24 | def insert(self, a, b=None): 25 | """ takes a hash of object and inserts it in the 26 | data structure """ 27 | 28 | leadera = self.get_leader(a) 29 | leaderb = self.get_leader(b) 30 | 31 | if not b: 32 | # only one item is inserted 33 | if a not in self.leader: 34 | # a is not already in any set 35 | self.leader[a] = a 36 | self.group[a] = set([a]) 37 | return 38 | 39 | if leadera is not None: 40 | if leaderb is not None: 41 | if leadera == leaderb: return # Do nothing 42 | self.make_union(leadera, leaderb) 43 | else: 44 | # leaderb is none 45 | self.group[leadera].add(b) 46 | self.leader[b] = leadera 47 | else: 48 | if leaderb is not None: 49 | # leadera is none 50 | self.group[leaderb].add(a) 51 | self.leader[a] = leaderb 52 | else: 53 | self.leader[a] = self.leader[b] = a 54 | self.group[a] = set([a, b]) 55 | 56 | def get_leader(self, a): 57 | return self.leader.get(a) 58 | 59 | def count_groups(self): 60 | """ returns a count of the number of groups/sets in the 61 | data structure""" 62 | return len(self.group) 63 | 64 | def make_union(self, leadera, leaderb): 65 | """ takes union of two sets with leaders, leadera and leaderb 66 | in O(nlogn) time """ 67 | if leadera not in self.group or leaderb not in self.group: 68 | raise Exception("Invalid leader specified leadera -%s, leaderb - %s" % (leadera, leaderb)) 69 | groupa = self.group[leadera] 70 | groupb = self.group[leaderb] 71 | if len(groupa) < len(groupb): 72 | # swap a and b if a is a smaller set 73 | leadera, groupa, leaderb, groupb = leaderb, groupb, leadera, groupa 74 | groupa |= groupb # taking union of a with b 75 | del self.group[leaderb] # delete b 76 | for k in groupb: 77 | self.leader[k] = leadera 78 | 79 | if __name__ == "__main__": 80 | uf = UnionFind() 81 | uf.insert("a", "b") 82 | --------------------------------------------------------------------------------