├── .DS_Store ├── .gitignore ├── README.md ├── __init__.py ├── algorithm-questions ├── backtrack │ ├── __init__.py │ ├── knights_tour.py │ ├── n_queen.py │ ├── rat_in_maze.py │ └── sudoku.py ├── dynamic_programming │ ├── 0_1_knapsack.py │ ├── binomial_coefficient.py │ ├── coin_change.py │ ├── cutting_a_rod.py │ ├── edit_distance.py │ ├── egg_dropping_puzzle.py │ ├── floyd_warshall.py │ ├── largest_independent_set_dynamic.py │ ├── largest_independent_set_recursive.py │ ├── longest_common_subsequence.py │ ├── longest_increasing_subsequence.py │ ├── longest_palindromic_subsequence.py │ ├── matrix_chain_multiplication.py │ ├── max_length_chain_of_pairs.py │ ├── max_square_sub_matrix_all_1.py │ ├── maximum_sum_increasing_subsequence.py │ ├── min_cost_path.py │ ├── minimum_jumps_to_reach_end.py │ ├── nth_stairs_problem.py │ ├── optimal_binary_search_tree.py │ ├── palindromic_partitioning.py │ ├── partition_problem.py │ └── subset_sum.py ├── searching │ ├── __init__.py │ ├── binary_search.py │ ├── linear_search │ └── ternary_search.py └── sorting │ ├── __init__.py │ ├── bubble_sort.py │ ├── bucket_sort.py │ ├── counting_sort.py │ ├── heap_sort.py │ ├── insertion_sort.py │ ├── merge_sort.py │ ├── quick_sort.py │ ├── radix_sort.py │ └── selection_sort.py ├── cracking_the_coding_interview ├── .DS_Store ├── __init__.py ├── array_and_string │ ├── .DS_Store │ ├── README.md │ ├── check_permutation_of_palindrome.py │ ├── check_permutation_strings.py │ ├── isSubstring.py │ ├── is_unique_characters.py │ ├── oneEditAway.py │ ├── rotate_matrix_by_90.py │ ├── string_compression.py │ ├── urlify.py │ └── zeroMatrix.py ├── linked_list │ ├── README.md │ ├── __init__.py │ ├── delete_middle_node.py │ ├── detect_loop.py │ ├── detect_palindrome.py │ ├── intersection_linked_lists.py │ ├── kth_from_end.py │ ├── linked_list.py │ ├── partition.py │ ├── remove_duplicates.py │ └── sum_linked_lists.py ├── moderate_problems │ ├── intersection.py │ ├── smallest_difference.py │ └── word_frequencies.py ├── recursion_and_dynamic_programming │ ├── coins.py │ ├── magic_index.py │ ├── robot_in_a_grid.py │ ├── step_count.py │ └── tower_of_hanoi.py ├── sorting_helper │ ├── __init__.py │ └── bubble_sort.py ├── stack_and_queue │ ├── README.md │ ├── queue.py │ ├── queue_via_stack.py │ ├── sort_stack.py │ ├── stack.py │ └── stack_min.py └── trees_and_graphs │ ├── .DS_Store │ ├── BSTSequence.py │ ├── README.md │ ├── __init__.py │ ├── adjacency_list_graph.py │ ├── binary_search_tree.py │ ├── binary_tree.py │ ├── build_order.py │ ├── check_balanced_tree.py │ ├── check_if_bst.py │ ├── first_common_ancestor.py │ ├── flood_fill.py │ ├── graph.py │ ├── inorder_successor.py │ ├── level_tree.py │ ├── linked_list.py │ ├── min_heap.py │ ├── minimal_tree.py │ ├── path_between_two_nodes.py │ ├── prims_minimum_spanning_tree.py │ ├── queue.py │ ├── random_node.py │ └── tree.py ├── data-structure-questions ├── array │ ├── bitonic_search.py │ ├── count_duplicate.py │ ├── first_and_last_occurence_in_sorted_array.py │ └── majority_element.py ├── binary_search_tree │ ├── bst.py │ └── lowest_common_ancestor_bst.py ├── binary_tree │ ├── __init__.py │ ├── binary_tree.py │ ├── binary_tree_diameter.py │ ├── binary_tree_to_doubly_link_list.py │ ├── boundary_traversal.py │ ├── check_if_all_leaves_are_at_same_level.py │ ├── check_if_bst.py │ ├── check_if_child_sum_property.py │ ├── check_if_tree_is_complete_tree.py │ ├── check_if_tree_is_subtree_of_another.py │ ├── check_if_tree_is_sumtree.py │ ├── connect_nodes.py │ ├── connect_nodes_extending_level_order_traversal.py │ ├── construct_binary_tree_from_linked_list.py │ ├── construct_special_tree_from_inorder.py │ ├── construct_special_tree_from_preorder.py │ ├── construct_tree_from_inorder_preorder.py │ ├── construct_tree_from_preorder.py │ ├── construct_tree_from_preorder_postorder.py │ ├── construct_tree_from_sorted_array.py │ ├── convert_binary_tree_to_circular_doubly_linked_list.py │ ├── convert_to_children_sum_property.py │ ├── convert_to_sumtree.py │ ├── convert_tree_to_linked_list.py │ ├── count_leaf_nodes.py │ ├── deepest_left_leaf_node.py │ ├── diameter.py │ ├── double_tree.py │ ├── duplicate_subtree_in_binary_tree.py │ ├── extract_leaves_of_binary_tree.py │ ├── find_the_closest_leaf_in_binary_tree.py │ ├── foldable_binary_tree.py │ ├── half_tree.py │ ├── identical_trees.py │ ├── inorder_successor.py │ ├── inorder_without_recursion.py │ ├── is_height_balanced_tree.py │ ├── isomorphic_trees.py │ ├── iterative_postorder_traversal_using_two_stacks.py │ ├── iterative_preorder.py │ ├── iteratively_search_element_in_binary_tree.py │ ├── left_leaves_sum.py │ ├── left_view_of_tree.py │ ├── level_of_node.py │ ├── level_order_traversal.py │ ├── level_order_traversal_line_by_line.py │ ├── lowest_common_ancestor_binary_tree.py │ ├── max_width.py │ ├── max_width_non_recursive.py │ ├── maximum_sum_root_to_leaf_path.py │ ├── mirror_binary_tree.py │ ├── morris_traversal_inorder.py │ ├── morris_traversal_preorder.py │ ├── print_all_ancestors_of_node.py │ ├── print_all_nodes_at_k_distance.py │ ├── print_tree_vertical_order.py │ ├── reverse_alternate_levels.py │ ├── reverse_level_order_traversal.py │ ├── root_to_leaf_path.py │ ├── root_to_leaf_pathsum_equal_to_a_given_number.py │ ├── spiral_order_traversal.py │ └── vertical_sum.py ├── graph │ ├── Boruvka_mst.py │ ├── __init__.py │ ├── algorithms │ │ ├── __init__.py │ │ ├── bellman_ford.py │ │ ├── breadth_first_search.py │ │ ├── depth_first_traversal.py │ │ ├── dijkstra_algorithm.py │ │ ├── hamiltonian_path.py │ │ ├── kruskal_minimum_spanning_tree.py │ │ ├── topological_sort.py │ │ └── union_find_algorithm.py │ ├── check_bipartitie.py │ ├── cycle_detection_in_directed_graph.py │ ├── detect_cycle_direct_graph_using_colors.py │ ├── detect_cycle_in_undirected_graph.py │ ├── detect_cycle_in_undirected_graph_union_find.py │ ├── eulerian_path.py │ ├── graph_coloring_problem.py │ ├── hamiltonian_cycle.py │ ├── iterative_depth_first_traversal.py │ ├── longest_path.py │ ├── min_heap.py │ ├── mother_vertex.py │ ├── strongly_connected_components.py │ └── transitive_closure_of_a_graph.py ├── linked_list │ ├── README.mdown │ ├── __init__.py │ ├── add_two_numbers_in_linked_list.py │ ├── alternate_merge.py │ ├── check_if_linklist_is_palindrome.py │ ├── common_intersection_of_two_linked_list.py │ ├── delete_alternate_nodes.py │ ├── delete_m_after_n.py │ ├── detect_and_remove_loop.py │ ├── find_intersection_of_two_linked_list.py │ ├── initialize.py │ ├── kth_node_from_end.py │ ├── linked_list.py │ ├── reverse_alternate_k_nodes.py │ ├── rotate_linked_list.py │ ├── swap_alternate_nodes.py │ └── swap_k_beginning_and_k_end.py └── string │ ├── anagram.py │ ├── check_if_string_formed_from_interleavings.py │ ├── check_if_strings_are_anagrams.py │ ├── interleavings_of_two_strings.py │ ├── kmp_pattern_searching.py │ ├── longest_common_subsequence.py │ ├── longest_common_substring.py │ ├── longest_palindrome_substring.py │ ├── min_edit_distance.py │ ├── most_occuring_element_in_string.py │ ├── pattern_matching │ ├── kmp_algorithm.py │ └── naive_pattern_search.py │ ├── remove_duplicates.py │ └── sentence_reverse.py ├── data_structures ├── __init__.py ├── abstract_syntax_tree.py ├── adjacency_list_graph.py ├── binary_search_tree.py ├── binary_tree.py ├── graph.py ├── linked_list.py ├── queue.py └── stack.py ├── random_problems ├── LRUCache.py ├── alien_dictionary.py ├── flatten_dict.py ├── google.py ├── isValidSudoku.py ├── island_count.py ├── n_chocolates.py ├── pattern_matching.py ├── points_on_a_straight_line.py ├── smallest_permutation_larger_than_original_number.py └── smallest_sequence_with_given_primes.py ├── searching └── jump_search └── tests ├── __init__.py ├── searching_test.py └── sorting_test.py /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/charulagrl/data-structures-and-algorithms/79ccc0aa34d9e3eb508bfe64d1fcee9dd1ea5e98/.DS_Store -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.swp 3 | .DS_Store 4 | -------------------------------------------------------------------------------- /__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/charulagrl/data-structures-and-algorithms/79ccc0aa34d9e3eb508bfe64d1fcee9dd1ea5e98/__init__.py -------------------------------------------------------------------------------- /algorithm-questions/backtrack/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/charulagrl/data-structures-and-algorithms/79ccc0aa34d9e3eb508bfe64d1fcee9dd1ea5e98/algorithm-questions/backtrack/__init__.py -------------------------------------------------------------------------------- /algorithm-questions/backtrack/rat_in_maze.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | # Rat in a maze problem 4 | 5 | # Rat is allowed to move only forward and down 6 | 7 | def isSafe(board, x, y): 8 | 9 | if x < len(board) and y < len(board) and board[x][y]: 10 | return True 11 | 12 | return False 13 | 14 | def get_coordinates(current, i): 15 | if i == 0: 16 | x, y = current['x']+1, current['y'] 17 | elif i == 1: 18 | x, y = current['x'], current['y']+1 19 | 20 | return x, y 21 | 22 | def rat_in_maze(board, current, destination, solution): 23 | 24 | # if rat current coordinates matches with its destination coordinates 25 | if current['x'] == destination['x'] and current['y'] == destination['y']: 26 | return True 27 | else: 28 | for i in range(2): 29 | x, y = get_coordinates(current, i) 30 | # Check if the rat is allowed to move to these coordinates 31 | if isSafe(board, x, y): 32 | solution[x][y] = 1 33 | current['x'] = x 34 | current['y'] = y 35 | # Recusive calling the same function 36 | if rat_in_maze(board, current, destination, solution): 37 | return True 38 | # Backtrack 39 | solution[x][y] = 0 40 | if i==0: 41 | current['x'] = x-1 42 | if i==1: 43 | current['y'] = y-1 44 | 45 | size = 4 46 | board = [[1, 0, 0, 0], [1, 1, 0, 1], [0, 1, 0, 0], [1, 1, 1, 1]] 47 | 48 | # initialize source and destination coordinates 49 | source = {'x':0, 'y':0} 50 | destination = {'x':3, 'y':3} 51 | 52 | # initialize solution matrix with all 0's 53 | solution = [] 54 | for i in range(size): 55 | solution.append([0]*size) 56 | 57 | # initialize current position of rat with its source position 58 | current = source 59 | solution[source['x']][source['y']] = 1 60 | rat_in_maze(board, current, destination, solution) 61 | print solution 62 | -------------------------------------------------------------------------------- /algorithm-questions/backtrack/sudoku.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | # Sudoku 4 | 5 | def isSafe(board, size, row, col, value): 6 | 7 | if row < 0 or row >= size or col < 0 or col >= size: 8 | return False 9 | 10 | # check for all the columns for current row 11 | for i in range(size): 12 | if board[row][i] == value: 13 | return False 14 | 15 | # check for all the rows for current column 16 | for j in range(size): 17 | if board[j][col] == value: 18 | return False 19 | 20 | # check for all the values in its box 21 | row_in = row-row%3 22 | col_in = col-col%3 23 | for i in range(3): 24 | for j in range(3): 25 | if board[i+row_in][j+col_in] == value: 26 | return False 27 | 28 | return True 29 | 30 | def findUnassignedLocation(board, size): 31 | for i in range(size): 32 | for j in range(size): 33 | if board[i][j] == 0: 34 | return i, j 35 | else: 36 | return -1, -1 37 | 38 | def sudoku(board, size): 39 | 40 | # Look for unassigned locations 41 | row, col = findUnassignedLocation(board, size) 42 | 43 | if row == -1 and col == -1: 44 | return True 45 | else: 46 | for i in range(1, size+1): 47 | if isSafe(board, size, row, col, i): 48 | board[row][col] = i 49 | if sudoku(board, size): 50 | return True 51 | # Backtrack 52 | board[row][col] = 0 53 | 54 | return False 55 | 56 | 57 | # initialize a sudoku board 58 | board = [ 59 | [3, 0, 6, 5, 0, 8, 4, 0, 0], 60 | [5, 2, 0, 0, 0, 0, 0, 0, 0], 61 | [0, 8, 7, 0, 0, 0, 0, 3, 1], 62 | [0, 0, 3, 0, 1, 0, 0, 8, 0], 63 | [9, 0, 0, 8, 6, 3, 0, 0, 5], 64 | [0, 5, 0, 0, 9, 0, 6, 0, 0], 65 | [1, 3, 0, 0, 0, 0, 2, 5, 0], 66 | [0, 0, 0, 0, 0, 0, 0, 7, 4], 67 | [0, 0, 5, 2, 0, 6, 3, 0, 0] 68 | ] 69 | size = len(board) 70 | 71 | if sudoku(board, size): 72 | for i in board: 73 | print i 74 | else: 75 | print "Solution does not exist" -------------------------------------------------------------------------------- /algorithm-questions/dynamic_programming/binomial_coefficient.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | # Binomial Coefficient (Dynammic Programming) 4 | 5 | 6 | def binomial_coefficient(n, k): 7 | '''Calculating bionomial coefficient using recursion''' 8 | if k == 0 or k == n: 9 | return 1 10 | 11 | return binomial_coefficient(n-1, k-1) + binomial_coefficient(n-1, k) 12 | 13 | 14 | def binomial_coefficient_dynamic(n, k): 15 | '''Calculating bionomial coefficient using Dynammic Programming''' 16 | soln = [] 17 | for i in range(n+1): 18 | soln.append([0]*(k+1)) 19 | 20 | for i in range(n+1): 21 | for j in range(k+1): 22 | 23 | if j == 0 or j == n: 24 | soln[i][j] = 1 25 | 26 | else: 27 | soln[i][j] = soln[i-1][j-1] + soln[i-1][j] 28 | 29 | return soln[n][k] 30 | 31 | import unittest 32 | 33 | class MyTest(unittest.TestCase): 34 | def setUp(self): 35 | self.n = 5 36 | self.k = 2 37 | 38 | def test_maximum_sum_recursive(self): 39 | self.assertEqual(binomial_coefficient(self.n, self.k), 10) 40 | 41 | def test_maximum_sum_dynamic(self): 42 | self.assertEqual(binomial_coefficient_dynamic(self.n, self.k), 10) 43 | 44 | if __name__ == "__main__": 45 | unittest.main() 46 | -------------------------------------------------------------------------------- /algorithm-questions/dynamic_programming/floyd_warshall.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | # Floyd Warshall (Dynammic Programmic). Algorithm to find minimum distance between all the vertices 4 | 5 | import sys 6 | 7 | INF = sys.maxint 8 | 9 | def floyd_warshall(graph, V): 10 | soln = graph 11 | 12 | for k in range(V): 13 | for i in range(V): 14 | for j in range(V): 15 | if soln[i][j] > soln[i][k] + soln[k][j] and soln[i][k] != INF and soln[k][j] != INF: 16 | soln[i][j] = soln[i][k] + soln[k][j] 17 | 18 | print soln 19 | 20 | graph = [[0, 5, INF, 10], 21 | [INF, 0, 3, INF], 22 | [INF, INF, 0, 1], 23 | [INF, INF, INF, 0]] 24 | 25 | floyd_warshall(graph, 4) -------------------------------------------------------------------------------- /algorithm-questions/dynamic_programming/largest_independent_set_dynamic.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | # Largest Independent set problem i.e. A subset of all tree nodes is an independent set if there is no edge between any two nodes of the subset. 4 | 5 | # Dynamic Programming Solution 6 | 7 | class Node(object): 8 | 9 | def __init__(self, data): 10 | 11 | self.data = data 12 | self.left = None 13 | self.right = None 14 | self.liss = 0 15 | 16 | def largest_independent_set_problem(root): 17 | if not root: 18 | return 0 19 | 20 | if root.liss: 21 | return root.liss 22 | 23 | if not root.left and not root.right: 24 | return 1 25 | 26 | a = largest_independent_set_problem(root.left) + largest_independent_set_problem(root.right) 27 | 28 | b = 1 29 | if root.left: 30 | b += largest_independent_set_problem(root.left.left) + largest_independent_set_problem(root.left.right) 31 | 32 | if root.right: 33 | b += largest_independent_set_problem(root.right.left) + largest_independent_set_problem(root.right.right) 34 | 35 | root.liss = max(a, b) 36 | 37 | return root.liss 38 | 39 | def construct_binary_tree(): 40 | root = Node(26) 41 | root.left = Node(10) 42 | root.right = Node(3) 43 | root.left.left = Node(4) 44 | root.left.right = Node(6) 45 | root.right.right = Node(13) 46 | root.right.left = Node(5) 47 | 48 | return root 49 | 50 | root = construct_binary_tree() 51 | print largest_independent_set_problem(root) -------------------------------------------------------------------------------- /algorithm-questions/dynamic_programming/largest_independent_set_recursive.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | # Largest Independent set problem i.e. A subset of all tree nodes is an independent set if there is no edge between any two nodes of the subset. 4 | 5 | class Node(object): 6 | 7 | def __init__(self, data): 8 | 9 | self.data = data 10 | self.left = None 11 | self.right = None 12 | 13 | # Recursive solution 14 | def largest_independent_set_problem(root): 15 | if root is None: 16 | return 0 17 | 18 | if not root.left and not root.right: 19 | return 1 20 | 21 | a = largest_independent_set_problem(root.left) + largest_independent_set_problem(root.right) 22 | 23 | b = 1 24 | if root.left: 25 | b += largest_independent_set_problem(root.left.left) + largest_independent_set_problem(root.left.right) 26 | 27 | if root.right: 28 | b += largest_independent_set_problem(root.right.left) + largest_independent_set_problem(root.right.right) 29 | 30 | return max(a, b) 31 | 32 | def construct_binary_tree(): 33 | root = Node(26) 34 | root.left = Node(10) 35 | root.right = Node(3) 36 | root.left.left = Node(4) 37 | root.left.right = Node(6) 38 | root.right.right = Node(13) 39 | root.right.left = Node(5) 40 | 41 | return root 42 | 43 | root = construct_binary_tree() 44 | print largest_independent_set_problem(root) 45 | 46 | -------------------------------------------------------------------------------- /algorithm-questions/dynamic_programming/max_length_chain_of_pairs.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | # Maximum Length Chain of Pairs 4 | 5 | def max_length_chain(arr, length): 6 | 7 | if length == 0: 8 | return 0 9 | 10 | if length == 1: 11 | return 1 12 | 13 | if length == 2: 14 | if arr[length-1][0] > arr[0][1]: 15 | return 2 16 | 17 | else: 18 | return 1 19 | 20 | else: 21 | return max(1+max_length_chain(arr, length-1), max_length_chain(arr, length-1)) 22 | 23 | def max_length_chain_dynamic(arr): 24 | 25 | length = len(arr) 26 | 27 | soln = [-1]*(length+1) 28 | soln[0] = 0 29 | soln[1] = 1 30 | soln[2] = 2 if arr[1][0] > arr[0][1] else 1 31 | for L in range(3, length+1): 32 | for i in range(L): 33 | if soln[L] < soln[i] + 1 and arr[L-1][1] > arr[i-1][0]: 34 | soln[L] = soln[i] + 1 35 | 36 | print soln[length] 37 | 38 | arr = [[5, 24], [15, 25], [27, 40], [50, 60]] 39 | print max_length_chain(arr, len(arr)) 40 | max_length_chain_dynamic(arr) 41 | 42 | -------------------------------------------------------------------------------- /algorithm-questions/dynamic_programming/max_square_sub_matrix_all_1.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | # Maximum size square sub-matrix with all 1s 4 | 5 | max_square = -1000000 6 | 7 | # Recursive approach 8 | def sub_matrix(mat, i, j): 9 | global max_square 10 | if i == 0 or j == 0: 11 | return mat[i][j] 12 | 13 | else: 14 | if mat[i][j] == 1: 15 | left = sub_matrix(mat, i-1, j) 16 | top = sub_matrix(mat, i, j-1) 17 | diagonal = sub_matrix(mat, i-1, j-1) 18 | res = min(left, min(top, diagonal)) + 1 19 | else: 20 | res = 0 21 | 22 | if res > max_square: 23 | max_square = res 24 | 25 | return res 26 | 27 | 28 | def sub_matrix_dynamic(mat, m, n): 29 | soln = [] 30 | 31 | for i in range(m): 32 | soln.append([None]*n) 33 | 34 | # Copying first columns elements to solution 35 | for i in range(m): 36 | soln[i][0] = mat[i][0] 37 | # Copying first row elements from matrix 38 | for j in range(n): 39 | soln[0][j] = mat[0][j] 40 | 41 | for i in range(1, m): 42 | for j in range(1, n): 43 | if mat[i][j] == 1: 44 | soln[i][j] = min(soln[i-1][j], min(soln[i-1][j-1], soln[i][j-1])) + 1 45 | else: 46 | soln[i][j] = 0 47 | 48 | 49 | maximum = soln[0][0] 50 | for i in range(m): 51 | for j in range(n): 52 | if soln[i][j] > maximum: 53 | maximum = soln[i][j] 54 | 55 | return maximum 56 | 57 | 58 | mat = [[0, 1, 1, 0, 1], 59 | [1, 1, 0, 1, 0], 60 | [0, 1, 1, 1, 0], 61 | [1, 1, 1, 1, 0], 62 | [1, 1, 1, 1, 1], 63 | [0, 0, 0, 0, 0]] 64 | 65 | sub_matrix(mat, 4, 4) 66 | print max_square 67 | print "Solving by DP" 68 | print sub_matrix_dynamic(mat, 5, 5) -------------------------------------------------------------------------------- /algorithm-questions/dynamic_programming/maximum_sum_increasing_subsequence.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | # Maximum Sum Increasing subsequence 4 | 5 | import sys 6 | 7 | max_sum = -sys.maxint 8 | 9 | # Time complexity: O(2 ^ n) which is the total number of subsequences possible 10 | def maximum_sum_recursive(array, index): 11 | '''Calculating maximum sum increasing subsequence using recursion''' 12 | global max_sum 13 | 14 | # IF there is only one element present. 15 | if index == 1: 16 | return array[index-1] 17 | 18 | else: 19 | max_value = -sys.maxint 20 | 21 | for i in range(1, index): 22 | res = maximum_sum_recursive(array, i) 23 | 24 | if array[i-1] < array[index-1] and res + array[index-1] > max_value: 25 | max_value = res + array[index-1] 26 | 27 | if max_value > max_sum: 28 | max_sum = max_value 29 | 30 | 31 | return max_value 32 | 33 | # Time complexity: O(n*n) 34 | def maximum_sum_dynamic(arr): 35 | '''Calculating maximum sum increasing subsequence using dynamic programming''' 36 | soln = [-sys.maxint] * len(arr) 37 | 38 | for i in range(len(arr)): 39 | soln[i] = arr[i] 40 | 41 | for i in range(1, len(arr)): 42 | for j in range(i): 43 | if arr[j] < arr[i] and soln[j] + arr[i] > soln[i]: 44 | soln[i] = soln[j] + arr[i] 45 | 46 | 47 | return max(soln) 48 | 49 | import unittest 50 | 51 | class MyTest(unittest.TestCase): 52 | def setUp(self): 53 | self.sequence = [1, 101, 2, 3, 100, 4, 5] 54 | self.length = len(self.sequence) 55 | 56 | def test_maximum_sum_recursive(self): 57 | maximum_sum_recursive(self.sequence, self.length) 58 | self.assertEqual(max_sum, 106) 59 | 60 | def test_maximum_sum_dynamic(self): 61 | self.assertEqual(maximum_sum_dynamic(self.sequence), 106) 62 | 63 | if __name__ == "__main__": 64 | unittest.main() 65 | -------------------------------------------------------------------------------- /algorithm-questions/dynamic_programming/nth_stairs_problem.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | # nth stairs problem 4 | 5 | # Recursive implementation 6 | def n_stairs(N): 7 | if N == 0 or N == 1 or N == 2: 8 | return N 9 | 10 | else: 11 | return n_stairs(N-1) + n_stairs(N-2) 12 | 13 | # Implementation through Dynamic Programming 14 | def n_stairs_dynamic(N): 15 | soln = [-1] * N 16 | 17 | soln[0] = 1 18 | soln[1] = 2 19 | 20 | for i in range(2, N): 21 | soln[i] = soln[i-1] + soln[i-2] 22 | 23 | return soln[N-1] 24 | 25 | if __name__ == "__main__": 26 | N = 4 27 | print n_stairs(4) 28 | print n_stairs_dynamic(N) 29 | -------------------------------------------------------------------------------- /algorithm-questions/dynamic_programming/palindromic_partitioning.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | # Palindrome partitioning 4 | 5 | import sys 6 | 7 | # Function to check if a string is palindrome or not 8 | def is_palindrome(string, st_in, end_in): 9 | 10 | if st_in == end_in: 11 | return True 12 | 13 | if string[st_in] == string[end_in] and is_palindrome(string, st_in+1, end_in-1): 14 | return True 15 | 16 | else: 17 | return False 18 | 19 | # Recursive approach 20 | def palindromic_partitioning(string, st_in, end_in): 21 | if st_in == end_in: 22 | return 0 23 | 24 | if is_palindrome(string, st_in, end_in): 25 | return 0 26 | 27 | else: 28 | minimum = sys.maxint 29 | for i in range(st_in, end_in): 30 | res = palindromic_partitioning(string, st_in, i) + 1 + palindromic_partitioning(string, i+1, end_in) 31 | 32 | if res < minimum: 33 | minimum = res 34 | 35 | return minimum 36 | 37 | # Dynamic Programming approach 38 | def palindromic_partitioning_dynamic(string): 39 | n = len(string) 40 | 41 | C = [] 42 | P = [] 43 | for i in range(n): 44 | C.append([None]*n) 45 | P.append([None]*n) 46 | 47 | for i in range(n): 48 | C[i][i] = 0 49 | P[i][i] = True 50 | 51 | for L in range(2, n+1): 52 | for i in range(n-L+1): 53 | j = i+L-1 54 | if L == 2: 55 | P[i][j] = (string[i] == string[j]) 56 | else: 57 | P[i][j] = (string[i] == string[j]) and P[i+1][j-1] 58 | 59 | if P[i][j]: 60 | C[i][j] = 0 61 | else: 62 | C[i][j] = sys.maxint 63 | for k in range(i, j): 64 | res = C[i][k] + C[k+1][j] + 1 65 | if res < C[i][j]: 66 | C[i][j] = res 67 | 68 | return C[0][n-1] 69 | 70 | a = "ababbbabbababa" 71 | print palindromic_partitioning(a, 0, len(a)-1) 72 | print "Solving by DP" 73 | print palindromic_partitioning_dynamic(a) 74 | -------------------------------------------------------------------------------- /algorithm-questions/dynamic_programming/partition_problem.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | # Partition Problem 4 | 5 | # Recursive implementation 6 | def partition_problem(arr, index, sum_val): 7 | 8 | if sum_val == 0: 9 | return True 10 | 11 | if index == 0 and sum_val != 0: 12 | return False 13 | 14 | if arr[index] > sum_val: 15 | return partition_problem(arr, index-1, sum_val) 16 | 17 | else: 18 | return partition_problem(arr, index-1, sum_val-arr[index]) or partition_problem(arr, index-1, sum_val) 19 | 20 | 21 | # Dynammic Programming Implementation 22 | def partition_problem_dynamic(arr, sum_val): 23 | 24 | n = len(arr) 25 | soln = [] 26 | 27 | for i in range(n+1): 28 | soln.append([None]*(sum_val+1)) 29 | 30 | # When sum value is empty 31 | for i in range(n+1): 32 | soln[i][0] = True 33 | 34 | # When arr index is 0 35 | for j in range(1, sum_val+1): 36 | soln[0][j] = False 37 | 38 | for i in range(1, n+1): 39 | for j in range(1, sum_val+1): 40 | if arr[i-1] > j: 41 | soln[i][j] = soln[i-1][j] 42 | 43 | else: 44 | soln[i][j] = soln[i-1][j-arr[i-1]] or soln[i-1][j] 45 | 46 | return soln[n][sum_val] 47 | 48 | 49 | a = [1, 5, 10] 50 | 51 | sum_val = sum(a) 52 | 53 | if sum_val % 2 == 1: 54 | print False 55 | else: 56 | print partition_problem(a, len(a)-1, sum_val/2) 57 | print "Solving by DP" 58 | print partition_problem_dynamic(a, sum_val/2) -------------------------------------------------------------------------------- /algorithm-questions/dynamic_programming/subset_sum.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | # Subset sum problem 4 | 5 | # Recursive implementation 6 | def subset_sum(arr, sum_value): 7 | n = len(arr) 8 | 9 | if sum_value == 0: 10 | return True 11 | 12 | if n == 0 and sum_value != 0: 13 | return False 14 | 15 | if arr[n-1] > sum_value: 16 | return subset_sum(arr[0:-1], sum_value) 17 | 18 | else: 19 | return subset_sum(arr[0:-1], sum_value-arr[-1]) or subset_sum(arr[0:-1], sum_value) 20 | 21 | 22 | # Dynammic Programming implementation 23 | def subset_sum_dynamic(arr, sum_value): 24 | n = len(arr) 25 | 26 | soln = [] 27 | 28 | for i in range(n+1): 29 | soln.append([None]*(sum_value+1)) 30 | 31 | # when sum_value is empty 32 | for i in range(0, n+1): 33 | soln[i][0] = True 34 | 35 | for j in range(sum_value+1): 36 | soln[0][j] = False 37 | 38 | for i in range(1, n+1): 39 | for j in range(1, sum_value+1): 40 | if arr[i-1] > j: 41 | soln[i][j] = soln[i-1][j] 42 | 43 | else: 44 | soln[i][j] = soln[i-1][j] or soln[i-1][j-arr[i-1]] 45 | 46 | return soln[n][sum_value] 47 | 48 | 49 | arr = [1, 5, 10] 50 | sum_value = 8 51 | print subset_sum(arr, sum_value) 52 | print subset_sum_dynamic(arr, sum_value) -------------------------------------------------------------------------------- /algorithm-questions/searching/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/charulagrl/data-structures-and-algorithms/79ccc0aa34d9e3eb508bfe64d1fcee9dd1ea5e98/algorithm-questions/searching/__init__.py -------------------------------------------------------------------------------- /algorithm-questions/searching/binary_search.py: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | ''' 4 | Binary Search is a searching algorithm that is very efficient and most commonly used techniques used in solving problems. 5 | 6 | Binary search divides the problem into half at each iteration. It works on a sorted set, and the number of iterations can 7 | always be reduced on the basis of value that is being searched. 8 | 9 | Complexity: O(log n) 10 | 11 | 12 | ''' 13 | 14 | def binary_search(low, high, key, array): 15 | ''' Search for the key and return the index of the key. 16 | If not found, returns -1''' 17 | while(low <= high): 18 | mid = (low + high) / 2 19 | 20 | if array[mid] == key: 21 | return mid 22 | 23 | elif array[mid] > key: 24 | low = mid + 1 25 | 26 | elif array[mid] < key: 27 | high = mid - 1 28 | 29 | 30 | return -1 31 | -------------------------------------------------------------------------------- /algorithm-questions/searching/linear_search: -------------------------------------------------------------------------------- 1 | def linearSearch(arr,key): #key be the element to be found 2 | n = len(arr) 3 | for i in range(0,n): 4 | if(arr[i] == key): 5 | return i 6 | return -1 7 | -------------------------------------------------------------------------------- /algorithm-questions/searching/ternary_search.py: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | ''' 4 | Ternary search is similar to linear and binary search that is used to determine the position of an element in a list. 5 | In binary search, the sorted array is divided into two parts while in ternary search the sorted array is divided into three 6 | parts. 7 | 8 | It is a divide and conquer algorithm where in each iteration, it neglects 2/3 of the part and repeats the same operation with 9 | the remaining 1/3. 10 | 11 | Complexity: (log N where base is 3) 12 | ''' 13 | 14 | def ternary_search(left, right, key, array): 15 | 16 | if (left <= right): 17 | midl = left + (right - left) / 3 18 | midr = right - (right - left) / 3 19 | 20 | if array[midl] == key: 21 | return midl 22 | 23 | if array[midr] == key: 24 | return midr 25 | 26 | if array[midl] > key: 27 | return ternary_search(left, midl-1, key, array) 28 | 29 | if array[midr] < key: 30 | return ternary_search(midr+1, right, key, array) 31 | 32 | else: 33 | return ternary_search(midl+1, midr-1, key, array) 34 | 35 | return -1 36 | -------------------------------------------------------------------------------- /algorithm-questions/sorting/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/charulagrl/data-structures-and-algorithms/79ccc0aa34d9e3eb508bfe64d1fcee9dd1ea5e98/algorithm-questions/sorting/__init__.py -------------------------------------------------------------------------------- /algorithm-questions/sorting/bubble_sort.py: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | ''' 4 | Bubble Sort 5 | 6 | Bubble sort is based on the idea of repeatedly comparing pairs of adjacent elements and then swapping their positions if 7 | they exist in the wrong order. 8 | 9 | Complexity: O(n*n) 10 | ''' 11 | 12 | def bubble_sort(array): 13 | 14 | for i in range(len(array)-1): 15 | for j in range(i, len(array)): 16 | if array[i] > array[j]: 17 | array[i], array[j] = array[j], array[i] 18 | 19 | return array 20 | -------------------------------------------------------------------------------- /algorithm-questions/sorting/bucket_sort.py: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 -------------------------------------------------------------------------------- /algorithm-questions/sorting/counting_sort.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/charulagrl/data-structures-and-algorithms/79ccc0aa34d9e3eb508bfe64d1fcee9dd1ea5e98/algorithm-questions/sorting/counting_sort.py -------------------------------------------------------------------------------- /algorithm-questions/sorting/heap_sort.py: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | class MaxHeap(object): 4 | def __init__(self, size=None, array=None): 5 | self.size = size 6 | self.array = array 7 | 8 | 9 | def swap(a, b): 10 | temp = a 11 | a = b 12 | b = a 13 | 14 | def maxHeapify(maxheap, index): 15 | largest = index 16 | left = 2*index+1 17 | right = 2*index+2 18 | 19 | if left < maxheap.size and maxheap.array[left] > maxheap.array[largest]: 20 | largest = left 21 | if right < maxheap.size and maxheap.array[right] > maxheap.array[largest]: 22 | largest = right 23 | 24 | if largest != index: 25 | maxheap.array[index], maxheap.array[largest] = maxheap.array[largest], maxheap.array[index] 26 | maxHeapify(maxheap, largest) 27 | 28 | def createAndBuildHeap(array): 29 | maxHeap = MaxHeap(len(array), array) 30 | 31 | for i in range((maxHeap.size-2)/2, -1, -1): 32 | maxHeapify(maxHeap, i) 33 | 34 | return maxHeap 35 | 36 | def heapSort(array): 37 | maxHeap = createAndBuildHeap(array) 38 | 39 | while (maxHeap.size > 1): 40 | maxHeap.array[maxHeap.size-1], maxHeap.array[0]=maxHeap.array[0], maxHeap.array[maxHeap.size-1] 41 | 42 | maxHeap.size -= 1 43 | 44 | maxHeapify(maxHeap, 0) 45 | 46 | 47 | arr = [12, 11, 13, 5, 6, 7, 3, 55, 21, 77, 4] 48 | 49 | print (arr) 50 | 51 | heapSort(arr) 52 | 53 | print arr -------------------------------------------------------------------------------- /algorithm-questions/sorting/insertion_sort.py: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | ''' 4 | Insertion sort 5 | 6 | Insertion sort is based on the idea that one element from the input elements is consumed in each iteration to find 7 | its correct position i.e. the position to which it belongs in a sorted array. 8 | 9 | Time complexity: O(n*n) 10 | ''' 11 | 12 | def insertion_sort(array): 13 | 14 | for i in range(1, len(array)): 15 | 16 | current_value = array[i] 17 | position = i 18 | 19 | while ( position > 0 and current_value < array[position-1]): 20 | array[position] = array[position-1] 21 | position = position-1 22 | 23 | array[position] = current_value 24 | 25 | return array 26 | -------------------------------------------------------------------------------- /algorithm-questions/sorting/merge_sort.py: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | ''' 4 | Merge sort 5 | 6 | Merge sort is a divide-and-conquer algorithm based on the idea of breaking down a list into several sub-lists until each 7 | sublist consists of a single element and merging those sublists in a manner that results into a sorted list. 8 | 9 | 10 | Time Complexity: O(N * logN) 11 | ''' 12 | 13 | def merge(a, b): 14 | ''' Merge the two arrays in sorted order''' 15 | p = 0 16 | q = 0 17 | 18 | new_array = [] 19 | # newlist.append is a function reference that is reevaluated each time through the loop 20 | # Hence, storing it in a variable is a better idea 21 | append = new_array.append 22 | 23 | while (p < len(a) and q < len(b)): 24 | if a[p] < b[q]: 25 | append(a[p]) 26 | p += 1 27 | 28 | else: 29 | append(b[q]) 30 | q += 1 31 | 32 | while (q < len(b)): 33 | append(b[q]) 34 | q += 1 35 | 36 | while(p < len(a)): 37 | append(a[p]) 38 | p += 1 39 | 40 | return new_array 41 | 42 | def merge_sort(array): 43 | length = len(array) 44 | 45 | if length == 1: 46 | return array 47 | 48 | mid = length / 2 49 | a = array[:mid] 50 | b = array[mid:] 51 | 52 | # Call recursion on the remaining elements 53 | l1 = merge_sort(a) 54 | l2 = merge_sort(b) 55 | 56 | # Merge the two arrays 57 | return merge(l1, l2) 58 | 59 | a = [12, 11, 13, 5, 6, 7, 3, 55, 21, 77, 4] 60 | a = merge_sort(a) 61 | print a 62 | -------------------------------------------------------------------------------- /algorithm-questions/sorting/quick_sort.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | ''' 4 | Quick sort 5 | 6 | Quick sort is based on the divide-and-conquer approach based on the idea of choosing one element as a pivot element 7 | and partitioning the array around it such that: Left side of pivot contains all the elements that are less than the pivot 8 | element Right side contains all elements greater than the pivot. 9 | ''' 10 | 11 | -------------------------------------------------------------------------------- /algorithm-questions/sorting/radix_sort.py: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 -------------------------------------------------------------------------------- /algorithm-questions/sorting/selection_sort.py: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | ''' 4 | Selection sort 5 | 6 | The Selection sort algorithm is based on the idea of finding the minimum or maximum element in an unsorted array 7 | and then putting it in its correct position in a sorted array. 8 | 9 | Complexity: O(n*n) 10 | ''' 11 | 12 | def selection_sort(array): 13 | ''' Sort a list using selection sort algorithm''' 14 | for i, _ in enumerate(array): 15 | # Initialize smallest element and index 16 | index = i 17 | for j in range(i, len(array)): 18 | if array[j] < array[index]: 19 | index = j 20 | 21 | if index != i: 22 | array[i], array[index] = array[index], array[i] 23 | 24 | return array 25 | -------------------------------------------------------------------------------- /cracking_the_coding_interview/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/charulagrl/data-structures-and-algorithms/79ccc0aa34d9e3eb508bfe64d1fcee9dd1ea5e98/cracking_the_coding_interview/.DS_Store -------------------------------------------------------------------------------- /cracking_the_coding_interview/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/charulagrl/data-structures-and-algorithms/79ccc0aa34d9e3eb508bfe64d1fcee9dd1ea5e98/cracking_the_coding_interview/__init__.py -------------------------------------------------------------------------------- /cracking_the_coding_interview/array_and_string/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/charulagrl/data-structures-and-algorithms/79ccc0aa34d9e3eb508bfe64d1fcee9dd1ea5e98/cracking_the_coding_interview/array_and_string/.DS_Store -------------------------------------------------------------------------------- /cracking_the_coding_interview/array_and_string/README.md: -------------------------------------------------------------------------------- 1 | # Arrays and Strings 2 | 3 | * [Check if string contains all unique characters](https://github.com/charulagrl/Data-Structures-and-Algorithms/blob/master/Cracking_the_coding_interview/CrackingTheCodingInterview/array_and_string/is_unique_characters.py) 4 | * [Check if two strings are permutation of each other](https://github.com/charulagrl/Data-Structures-and-Algorithms/blob/master/Cracking_the_coding_interview/CrackingTheCodingInterview/array_and_string/check_permutation_strings.py) 5 | * [Urify a string](https://github.com/charulagrl/Data-Structures-and-Algorithms/blob/master/Cracking_the_coding_interview/CrackingTheCodingInterview/array_and_string/urlify.py) 6 | * [Check if string is a permutation of palindrome](https://github.com/charulagrl/Data-Structures-and-Algorithms/blob/master/Cracking_the_coding_interview/CrackingTheCodingInterview/array_and_string/check_permutation_of_palindrome.py) 7 | * [Check if two strings are one edit away](https://github.com/charulagrl/Data-Structures-and-Algorithms/blob/master/Cracking_the_coding_interview/CrackingTheCodingInterview/array_and_string/oneEditAway.py) 8 | * [String Compression](https://github.com/charulagrl/Data-Structures-and-Algorithms/blob/master/Cracking_the_coding_interview/CrackingTheCodingInterview/array_and_string/string_compression.py) 9 | * [Rotate Matrix](https://github.com/charulagrl/Data-Structures-and-Algorithms/blob/master/CrackingTheCodingInterview/array_and_string/rotate_matrix_by_90.py) 10 | * [Matrix conversion to zero matrix](https://github.com/charulagrl/Data-Structures-and-Algorithms/blob/master/Cracking_the_coding_interview/CrackingTheCodingInterview/array_and_string/zeroMatrix.py) 11 | * [String Rotation](https://github.com/charulagrl/Data-Structures-and-Algorithms/blob/master/CrackingTheCodingInterview/array_and_string/isSubstring.py) -------------------------------------------------------------------------------- /cracking_the_coding_interview/array_and_string/check_permutation_strings.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | ''' 4 | 5 | Problem: Given two strings, check if one string is a permutation of another. 6 | 7 | Naive Approach: Get all combinations of one string and match with the other string, if it is similar or not. 8 | 9 | Time Complexity: O(n!) where n is the length of the string. 10 | 11 | Better Solution: Store all the characters of a string in a hash-map and keep the count of each character across each key. 12 | While parsing through the other string, reduce the count for each character in hash_map. If the final hash_map is empty, both 13 | the strings are permutation of each other. 14 | 15 | Time Complexity: O(n) where n is the length of string 16 | Space Complexity: O(n) 17 | 18 | ''' 19 | 20 | 21 | # function to check if two strings are permutation of each other 22 | def check_permutation(s1, s2): 23 | 24 | hash_map = {} 25 | 26 | for i in s1: 27 | hash_map.setdefault(i, 0) 28 | hash_map[i] += 1 29 | 30 | # loop through the second string 31 | for i in s2: 32 | # delete the value for each character found in hash_map 33 | if i in hash_map: 34 | if hash_map[i] > 1: 35 | hash_map[i] -= 1 36 | 37 | else: 38 | del hash_map[i] 39 | 40 | # if any character is not found in hash map, return False 41 | else: 42 | return False 43 | 44 | 45 | # check if the hash_map is empty 46 | if hash_map: 47 | return False 48 | else: 49 | return True 50 | 51 | # Writing unit-test for the above function 52 | import unittest 53 | 54 | class MyTest(unittest.TestCase): 55 | 56 | def test(self): 57 | self.assertEqual(check_permutation("aabcbaah", "haabcbaa"), True) 58 | self.assertEqual(check_permutation("aabcccaaa", "abcacaac"), False) 59 | 60 | unittest.main() 61 | -------------------------------------------------------------------------------- /cracking_the_coding_interview/array_and_string/isSubstring.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | ''' 4 | 5 | Problem: Program to check if one word is a substring of another. If you have two strings s1 and s2, write code to check 6 | if s2 is a rotation of s1 using only one call to isSubstring. 7 | 8 | example: hoolahoop is a rotation of "lahoophoo" 9 | oophoolah 10 | ''' 11 | 12 | ''' 13 | Naive approach: Compare each character of the first string with the first character pf the second string. 14 | Whenever it matches then rotate the first string and compare the two strings. If the two strings are same then one 15 | is rotation of another. 16 | 17 | Time Complexity: O(n) We will have to concatenate string which is inefficient. 18 | ''' 19 | 20 | def string_rotation(s1, s2): 21 | 22 | length1 = len(s1) 23 | length2 = len(s2) 24 | 25 | if length1 != length2: 26 | return False 27 | 28 | for index in range(length1): 29 | if s1[index] == s2[0]: 30 | new_string = s1[index: length1] + s1[:index] 31 | if new_string == s2: 32 | return True 33 | 34 | return False 35 | 36 | ''' 37 | Approach 2: Add the second string to the same string and then find the first string in the second string. If you can 38 | find it, then it is a substring otherwise not. The criteria is s1 is always a substring of s2s2. 39 | 40 | Time Complexity: O(n) where n is the length of the string. 41 | 42 | ''' 43 | def isSubstring(s1, s2): 44 | 45 | s2s2 = s2 + s2 46 | 47 | # in checks if one string is part of another. 48 | if s1 in s2s2: 49 | return True 50 | 51 | else: 52 | return False 53 | 54 | 55 | # Writing unit-test for the above function 56 | import unittest 57 | 58 | class MyTest(unittest.TestCase): 59 | 60 | def test(self): 61 | self.assertEqual(string_rotation("hoolahoop", "lahoophoo"), True) 62 | self.assertEqual(string_rotation("waterbottle", "erbottlewat"), True) 63 | 64 | unittest.main() 65 | -------------------------------------------------------------------------------- /cracking_the_coding_interview/array_and_string/rotate_matrix_by_90.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | ''' 4 | 5 | Problem: An image is represented by N*N matrix, where each pixel is four bytes. Write a method to rotate matrix by 90 degrees. 6 | 7 | example: 8 | 1 2 3 4 13 9 5 1 9 | 5 6 7 8 ---> 14 10 6 2 10 | 9 10 11 12 15 11 7 3 11 | 13 14 15 16 16 12 8 4 12 | 13 | ''' 14 | 15 | ''' 16 | Approach 1: By taking an additional 2-D matrix and copying the elements from original matrix 17 | 18 | Complexity: O(n * 2) Space Complexity: O(n * 2) 19 | ''' 20 | def rotate_90(matrix): 21 | 22 | N = len(matrix) 23 | 24 | new_matrix = [] * N 25 | 26 | for i in range(N): 27 | new_matrix.append([0]*N) 28 | 29 | for i in range(N): 30 | for j in range(N): 31 | new_matrix[j][N-i-1] = matrix[i][j] 32 | 33 | return new_matrix 34 | 35 | ''' 36 | Approach 2: Rotating the matrix by in-place changing the values of the original matrix 37 | 38 | Complexity: O(n * 2) 39 | ''' 40 | 41 | # TODO : Implementation 42 | 43 | 44 | # Writing unit-test for the above function 45 | import unittest 46 | 47 | class MyTest(unittest.TestCase): 48 | 49 | def test(self): 50 | self.assertEqual(rotate_90([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]]), [[13, 9, 5, 1], [14, 10, 6, 2], [15, 11, 7, 3], [16, 12, 8, 4]]) 51 | 52 | unittest.main() 53 | -------------------------------------------------------------------------------- /cracking_the_coding_interview/linked_list/README.md: -------------------------------------------------------------------------------- 1 | # LinkedLists 2 | 3 | * [Remove duplicates](https://github.com/charulagrl/Data-Structures-and-Algorithms/blob/master/CrackingTheCodingInterview/linked_list/remove_duplicates.py) 4 | * [Return kth element to last](https://github.com/charulagrl/Data-Structures-and-Algorithms/blob/master/CrackingTheCodingInterview/linked_list/kth_from_end.py) 5 | * [Delete middle node](https://github.com/charulagrl/data-structures-and-algorithms/blob/master/cracking_the_coding_interview/linked_list/delete_middle_node.py) 6 | * [Partition a linked list](https://github.com/charulagrl/data-structures-and-algorithms/blob/master/cracking_the_coding_interview/linked_list/partition.py) 7 | * [Sum of lists](https://github.com/charulagrl/data-structures-and-algorithms/blob/master/cracking_the_coding_interview/linked_list/sum_linked_lists.py) 8 | * [Detect Palindrome](https://github.com/charulagrl/data-structures-and-algorithms/blob/master/cracking_the_coding_interview/linked_list/detect_palindrome.py) 9 | * [Find intersection of two linked list](https://github.com/charulagrl/data-structures-and-algorithms/blob/master/cracking_the_coding_interview/linked_list/intersection_linked_lists.py) 10 | * [Detect loop in linked list](https://github.com/charulagrl/data-structures-and-algorithms/blob/master/cracking_the_coding_interview/linked_list/detect_loop.py) -------------------------------------------------------------------------------- /cracking_the_coding_interview/linked_list/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/charulagrl/data-structures-and-algorithms/79ccc0aa34d9e3eb508bfe64d1fcee9dd1ea5e98/cracking_the_coding_interview/linked_list/__init__.py -------------------------------------------------------------------------------- /cracking_the_coding_interview/linked_list/delete_middle_node.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | ''' 4 | Problem: Implement an algorithm to delete node in the middle of a singly linked list, given only access to that node. 5 | 6 | Solution: When you are given the pointer to the node, change the value to the next pointer value and 7 | next pointer to the next of next pointer. 8 | 9 | P.S This will not work when the element is last node of the linked list 10 | 11 | Input: 33 4 8 41 9 6 13 5 4 12 | Output: 33 8 41 9 6 13 5 4 13 | ''' 14 | 15 | import linked_list 16 | 17 | def delete_middle_node(node): 18 | '''Delete a node in between the linked list.''' 19 | if (node == None or node.nextnode == None): 20 | return False 21 | 22 | next = node.nextnode 23 | node.data = next.data 24 | node.nextnode = next.nextnode 25 | 26 | return True 27 | 28 | 29 | if __name__ == "__main__": 30 | '''Initialize linked list and call delete middle node function''' 31 | llist = linked_list.initialize_linked_list() 32 | llist.print_list() 33 | 34 | k = delete_middle_node(llist.head.nextnode) 35 | 36 | llist.print_list() 37 | -------------------------------------------------------------------------------- /cracking_the_coding_interview/linked_list/detect_loop.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | ''' 4 | Problem: Given a circular linked list, implement an algorithm that returns node at the beginning of the loop. 5 | 6 | Solution: Take a slow pointer and a fast pointer and faster pointer will iterate twice as fast as slow pointer. If the two meets or 7 | slow pointer gets passed the fast pointer, there is a loop in the linked lists. 8 | 9 | For explaination: http://stackoverflow.com/questions/2936213/explain-how-finding-cycle-start-node-in-cycle-linked-list-work 10 | ''' 11 | 12 | import linked_list 13 | 14 | def detect_loop(linked_list): 15 | '''Detect loop in a linked list and return starting point of loop''' 16 | slow_ptr = linked_list.head 17 | fast_ptr = linked_list.head 18 | 19 | while(fast_ptr != None and fast_ptr.nextnode != None): 20 | # print slow_ptr.data, fast_ptr.data 21 | slow_ptr = slow_ptr.nextnode 22 | fast_ptr = fast_ptr.nextnode.nextnode 23 | 24 | if slow_ptr == fast_ptr: 25 | break 26 | 27 | # If slow and fast do not meet, then no loop exists 28 | if fast_ptr is None or fast_ptr.nextnode is None: 29 | return False 30 | 31 | slow_ptr = linked_list.head 32 | 33 | while(slow_ptr != fast_ptr): 34 | slow_ptr = slow_ptr.nextnode 35 | fast_ptr = fast_ptr.nextnode 36 | 37 | # Both pointer point to start of the loop 38 | return fast_ptr 39 | 40 | if __name__ == "__main__": 41 | llist = linked_list.initialize_linked_list() 42 | llist.print_list() 43 | 44 | current = llist.head 45 | 46 | while (current.nextnode is not None): 47 | current = current.nextnode 48 | 49 | current.nextnode = llist.head.nextnode.nextnode.nextnode 50 | print "We have put circle at", (llist.head.nextnode.nextnode.nextnode.data) 51 | start_node = detect_loop(llist) 52 | 53 | # If there is loop in the linked_list 54 | if start_node: 55 | print start_node.data 56 | else: 57 | print "Cycle not found" 58 | -------------------------------------------------------------------------------- /cracking_the_coding_interview/linked_list/detect_palindrome.py: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | ''' 4 | Problem: Write a program to check if a linked lists is a palindrome or not. 5 | 6 | Solution: Iterate upto half of the linked list and put the elements in a stack and pop element one by one and compare it with the 7 | rest of the linked list. If all elements matches, it is a palindrome. 8 | 9 | Example: 1->2->3->2->1 returns True 10 | 1->3->4->3->5 returns False 11 | 12 | # TODO: Solve this problem by reversing the linked list 13 | ''' 14 | 15 | import linked_list 16 | 17 | def check_palindrome(ll): 18 | '''Check if a linked list is a palindrome ''' 19 | slow_ptr = ll.head 20 | fast_ptr = ll.head 21 | stack = [] 22 | 23 | while(fast_ptr != None and fast_ptr.nextnode != None): 24 | # Append the half of the linked list into a stack 25 | stack.append(slow_ptr.data) 26 | slow_ptr = slow_ptr.nextnode 27 | fast_ptr = fast_ptr.nextnode.nextnode 28 | 29 | # To check if the linked list is odd length or even length 30 | if fast_ptr is None: 31 | second_half = slow_ptr 32 | else: 33 | second_half = slow_ptr.nextnode 34 | 35 | while (len(stack) != 0 and second_half != None): 36 | first_half = stack.pop(-1) 37 | if first_half != second_half.data: 38 | return False 39 | second_half = second_half.nextnode 40 | 41 | return True 42 | 43 | if __name__ == "__main__": 44 | # Check for the linked list (33 4 8 41 9 6 13 5 4) 45 | ll = linked_list.initialize_linked_list() 46 | ll.print_list() 47 | print check_palindrome(ll) 48 | 49 | ll = linked_list.LinkedList() 50 | ll.insert(4) 51 | ll.insert(5) 52 | ll.insert(13) 53 | ll.insert(13) 54 | ll.insert(5) 55 | ll.insert(4) 56 | ll.print_list() 57 | # Check for the linked list (4 5 13 13 5 4) 58 | print check_palindrome(ll) 59 | -------------------------------------------------------------------------------- /cracking_the_coding_interview/linked_list/remove_duplicates.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | ''' 4 | Problem: Implement a program to remove duplicates elements from an unsorted linked list 5 | 6 | Example: 2 -> 1 -> 7 -> 2 -> 9 -> 1 -> 8 7 | Result: 2 -> 1 -> 7 -> 9 -> 8 8 | 9 | Approach 1: Iterate through each element in a linked list and look for the same element in the list ahead. 10 | If found, delete that element. 11 | 12 | Time Complexity: O(n * n) 13 | 14 | Approach 2: Store the elements in a hash-map and if next time the same element is encountered, delete it. 15 | 16 | Time Complexity: O(n) Space Complexity: O(n) 17 | ''' 18 | 19 | import linked_list 20 | 21 | def remove_duplicates(head): 22 | ''' Remove duplicates using hash map ''' 23 | hash_map = {} 24 | current = head 25 | prev = None 26 | 27 | while (current): 28 | if current.data in hash_map: 29 | prev.nextnode = current.nextnode 30 | 31 | else: 32 | hash_map[current.data] = 1 33 | 34 | prev = current 35 | current = current.nextnode 36 | 37 | 38 | # Writing unit-test for the above function 39 | # import unittest 40 | 41 | # class MyTest(unittest.TestCase): 42 | 43 | # def test(self): 44 | # lList = linked_list.initialize_linked_list() 45 | # lList.print_list() 46 | # # self.assertEqual(string_rotation("hoolahoop", "lahoophoo"), True) 47 | # # self.assertEqual(string_rotation("waterbottle", "erbottlewat"), True) 48 | 49 | # unittest.main() 50 | 51 | lList = linked_list.initialize_linked_list() 52 | lList.print_list() -------------------------------------------------------------------------------- /cracking_the_coding_interview/moderate_problems/intersection.py: -------------------------------------------------------------------------------- 1 | # Given two straight line segments, find intersection point of two line segments 2 | 3 | def slope(p): 4 | x1 = p[0][0] 5 | x2 = p[1][0] 6 | y1 = p[0][1] 7 | y2 = p[1][1] 8 | 9 | return (y2-y1)/float(x2-x1) 10 | 11 | def intercept(p, m): 12 | return p[0][1] - (m * p[0][0]) 13 | 14 | def is_between(p, intersect_x, intersect_y): 15 | if intersect_x > p[0][0] and intersect_x < p[1][0] and intersect_y > p[0][1] and intersect_y < p[1][1]: 16 | return True 17 | 18 | return False 19 | 20 | def point_of_intersection(a, b): 21 | m1 = slope(a) 22 | m2 = slope(b) 23 | 24 | if m1 == m2: 25 | return None 26 | else: 27 | c1 = intercept(a, m1) 28 | c2 = intercept(b, m2) 29 | 30 | intersect_x = (c1 - c2) / (m2 - m1) 31 | intersect_y = m1 * intersect_x + c1 32 | 33 | # Check if intersection point is witin the line segment range 34 | if is_between(a, intersect_x, intersect_y) and is_between(b, intersect_x, intersect_y): 35 | return intersect_x, intersect_y 36 | 37 | return None 38 | 39 | print point_of_intersection([(1, 2), (3, 4)], [(5, 6), (8, 10)]) -------------------------------------------------------------------------------- /cracking_the_coding_interview/moderate_problems/smallest_difference.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | ''' 3 | Given the two array of integers, compute the pair of values with the smallest difference. 4 | Return the difference. 5 | 6 | Time complexity: O(AlogA + BlogB) 7 | ''' 8 | import sys 9 | 10 | def smallest_difference(array1, array2): 11 | if not array1 or not array2: 12 | return None 13 | 14 | else: 15 | array1.sort() 16 | array2.sort() 17 | length1 = len(array1) 18 | length2 = len(array2) 19 | 20 | difference = sys.maxint 21 | a = 0 22 | b = 0 23 | 24 | while a < length1 and b < length2: 25 | if abs(array1[a]-array2[b]) < difference: 26 | difference = abs(array1[a] - array2[b]) 27 | pair = [array1[a], array2[b]] 28 | 29 | if array1[a] < array2[b]: 30 | a += 1 31 | else: 32 | b += 1 33 | 34 | 35 | return pair 36 | 37 | print smallest_difference([1, 3, 15, 11, 2], [23, 127, 235, 19, 8]) 38 | -------------------------------------------------------------------------------- /cracking_the_coding_interview/moderate_problems/word_frequencies.py: -------------------------------------------------------------------------------- 1 | # find frequency word 2 | # in book 3 | 4 | 5 | def find_frequency(book, word): 6 | count = 0 7 | for each_word in book: 8 | if each_word == word: 9 | count += 1 -------------------------------------------------------------------------------- /cracking_the_coding_interview/recursion_and_dynamic_programming/coins.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | ''' 4 | Given an infinite number of quaters(25 cents), dimes(10 cents), nickels(5 cents), pennies(1 cent), write code 5 | to calculate the number of ways of representing them. 6 | ''' 7 | 8 | def count_ways_recursive(total, coins): 9 | if total < 0: 10 | return 0 11 | 12 | if total == 0: 13 | return 1 14 | 15 | if not coins and total: 16 | return 0 17 | 18 | return count_ways_recursive(total-coins[0], coins) + count_ways_recursive(total, coins[1:]) 19 | 20 | def count_ways_dynamic(total, coins): 21 | soln = [[0] * len(coins) for i in range(total+1)] 22 | 23 | for i in range(total+1): 24 | for j in range(len(coins)): 25 | if i == 0: 26 | soln[i][j] = 1 27 | 28 | else: 29 | if coins[j] <= i: 30 | soln[i][j] += soln[i-coins[j]][j] 31 | if j >= 1: 32 | soln[i][j] += soln[i][j-1] 33 | 34 | return soln[total][len(coins)-1] 35 | 36 | if __name__ == '__main__': 37 | print count_ways_recursive(100, [25, 10, 5, 1]) 38 | print count_ways_dynamic(100, [25, 10, 5, 1]) 39 | -------------------------------------------------------------------------------- /cracking_the_coding_interview/recursion_and_dynamic_programming/magic_index.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | ''' 4 | Magic Index 5 | 6 | A magic index in an array A[0 ... n-1] is defined to be an index such that A[i] = i. Given a sorted array of 7 | distict integers, write a method to find a magic index, if one exists, in array A. 8 | ''' 9 | 10 | def magic_index(array, start, end): 11 | 12 | if start > end: 13 | return -1 14 | 15 | mid = (start+end) / 2 16 | 17 | if array[mid] == mid: 18 | return mid 19 | 20 | if array[mid] > mid: 21 | return magic_index(array, start, mid-1) 22 | 23 | else: 24 | return magic_index(array, mid+1, end) 25 | 26 | array = [-40, -20, -1, 1, 2, 3, 5, 7, 9, 12, 13] 27 | print magic_index(array, 0, len(array)-1) 28 | 29 | ''' 30 | If the array does not contain distinct numbers 31 | ''' 32 | 33 | def magic_index_2(array, start, end): 34 | 35 | if start > end: 36 | return -1 37 | 38 | mid = (start+end) / 2 39 | 40 | if array[mid] == mid: 41 | return mid 42 | 43 | if array[mid] > mid: 44 | index = min(array[mid], mid-1) 45 | return magic_index(array, start, index) 46 | 47 | else: 48 | index = max(array[mid], mid+1) 49 | return magic_index(array, index, end) 50 | 51 | 52 | array = [-10, -5, 2, 2, 2, 3, 4, 7, 9, 12, 13] 53 | print magic_index_2(array, 0, len(array)-1) 54 | -------------------------------------------------------------------------------- /cracking_the_coding_interview/recursion_and_dynamic_programming/robot_in_a_grid.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | ''' 4 | Imagine a robot sitting on the upper left corner of the grid with r rows and c columns. The robot can only move 5 | in two directions, right and down, but certain cells are off limits such that the robot cannot step on them. 6 | Design an algorithm to find the path for the robot from the top left to the bottom right. 7 | ''' 8 | def find_path(cur_i, cur_j, grid, path): 9 | 10 | if cur_i < 0 or cur_j < 0: 11 | return False 12 | 13 | if grid[cur_i][cur_j] in ['X', 'P']: 14 | return False 15 | 16 | if (cur_i == 0 and cur_j == 0) or find_path(cur_i-1, cur_j, grid, path) or find_path(cur_i, cur_j-1, grid, path): 17 | path.append((cur_i, cur_j)) 18 | return True 19 | 20 | grid[cur_i][cur_j] = 'P' 21 | 22 | return False 23 | 24 | def find_path_dynamic(cur_i, cur_j, grid, path, cache): 25 | 26 | if cur_i < 0 or cur_j < 0: 27 | return False 28 | 29 | if grid[cur_i][cur_j] == 'X': 30 | return False 31 | 32 | key = (cur_i, cur_j) 33 | 34 | if key in cache: 35 | return cache[key] 36 | 37 | success = False 38 | if (cur_i == 0 and cur_j == 0) or find_path(cur_i-1, cur_j, grid, path) or find_path(cur_i, cur_j-1, grid, path): 39 | path.append((cur_i, cur_j)) 40 | success = True 41 | 42 | cache[(cur_i, cur_j)] = success 43 | return success 44 | 45 | if __name__ == "__main__": 46 | grid = [] 47 | for i in range(6): 48 | grid.append(['']*5) 49 | 50 | grid[0][2] = "X" 51 | grid[1][4] = "X" 52 | grid[2][3] = "X" 53 | grid[4][0] = "X" 54 | print grid 55 | path = [] 56 | find_path(len(grid)-1, len(grid[0])-1, grid, path) 57 | print path 58 | path = [] 59 | cache = {} 60 | find_path_dynamic(len(grid)-1, len(grid[0])-1, grid, path, cache) 61 | print path 62 | -------------------------------------------------------------------------------- /cracking_the_coding_interview/recursion_and_dynamic_programming/step_count.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | ''' 4 | Problem: A child is running up the staircase with n steps and can hop either 1 step, 2 steps, or 3 steps at a time. 5 | Implement a method to count how many possible ways the child can run up the stairs. 6 | ''' 7 | 8 | def step_recursive(n): 9 | ''' Recursive solution to count the possible ways to run up the stairs ''' 10 | if n < 0: 11 | return 0 12 | 13 | if n == 0: 14 | return 1 15 | 16 | else: 17 | return step_recursive(n-1) + step_recursive(n-2) + step_recursive(n-3) 18 | 19 | def step_dynamic(n): 20 | '''Count the number of ways to take n steps usng dynamic programming''' 21 | soln = [0] * (n+1) 22 | soln[0] = 1 23 | 24 | for i in range(1, n+1): 25 | if i >= 1: 26 | soln[i] += soln[i-1] 27 | if i >= 2: 28 | soln[i] += soln[i-2] 29 | if i >= 3: 30 | soln[i] += soln[i-3] 31 | 32 | return soln[n] 33 | 34 | if __name__ == "__main__": 35 | print "Counting possible ways using recursion" 36 | print step_recursive(4) 37 | print step_recursive(5) 38 | print step_recursive(6) 39 | print "Counting possible ways using dynamic programming" 40 | print step_dynamic(4) 41 | print step_dynamic(5) 42 | print step_dynamic(6) 43 | -------------------------------------------------------------------------------- /cracking_the_coding_interview/recursion_and_dynamic_programming/tower_of_hanoi.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | ''' 4 | Tower of Hanoi is a mathematical puzzle where we have three rods and n disks. The objective of the 5 | puzzle is to move the entire stack to another rod, obeying the following simple rules: 6 | 1) Only one disk can be moved at a time. 7 | 2) Each move consists of taking the upper disk from one of the stacks and placing it on top of another 8 | stack i.e. a disk can only be moved if it is the uppermost disk on a stack. 9 | 3) No disk may be placed on top of a smaller disk. 10 | ''' 11 | 12 | # Time complexity: o(2^n - 1) 13 | def move_disks(n, origin, destination, aux): 14 | if n == 1: 15 | print "Move disk 1 from %s to %s"%(origin, destination) 16 | return 17 | 18 | move_disks(n-1, origin, aux, destination) 19 | print "Move disk %s from %s to %s"%(n, origin, destination) 20 | move_disks(n-1, aux, destination, origin) 21 | 22 | move_disks(3, 'A', 'B', 'C') 23 | -------------------------------------------------------------------------------- /cracking_the_coding_interview/sorting_helper/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/charulagrl/data-structures-and-algorithms/79ccc0aa34d9e3eb508bfe64d1fcee9dd1ea5e98/cracking_the_coding_interview/sorting_helper/__init__.py -------------------------------------------------------------------------------- /cracking_the_coding_interview/sorting_helper/bubble_sort.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | ''' 4 | Problem: Sort a string using bubble sort algorithm 5 | 6 | ''' 7 | def sort(s): 8 | 9 | length = len(s) 10 | s = list(s) 11 | 12 | for i in range(length-1): 13 | for j in range(i+1, length): 14 | if s[i] > s[j]: 15 | s[j], s[i] = s[i], s[j] 16 | 17 | 18 | return s 19 | -------------------------------------------------------------------------------- /cracking_the_coding_interview/stack_and_queue/README.md: -------------------------------------------------------------------------------- 1 | # Stacks and Queues 2 | 3 | [Stack Implementation](https://github.com/charulagrl/data-structures-and-algorithms/blob/master/cracking_the_coding_interview/stack_and_queue/stack.py) and [Queue Implementation](https://github.com/charulagrl/data-structures-and-algorithms/blob/master/cracking_the_coding_interview/stack_and_queue/queue.py) 4 | 5 | * [Three in One]() 6 | * [Stack Min](https://github.com/charulagrl/data-structures-and-algorithms/blob/master/cracking_the_coding_interview/stack_and_queue/stack_min.py) 7 | * [Stack of Plates]() 8 | * [Queue via Stacks](https://github.com/charulagrl/data-structures-and-algorithms/blob/master/cracking_the_coding_interview/stack_and_queue/queue_via_stack.py) 9 | * [Sort Stack](https://github.com/charulagrl/data-structures-and-algorithms/blob/master/cracking_the_coding_interview/stack_and_queue/sort_stack.py) 10 | * [Animal Shelter]() -------------------------------------------------------------------------------- /cracking_the_coding_interview/stack_and_queue/queue.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | ''' 4 | Class to implement queue in Python 5 | 6 | Queue: A queue stores elements in a linear fashion such that they follow first-in-first-out(FIFO) order. The elements 7 | are added to the top of the queue and removed from the beginning of the queue. 8 | 9 | ''' 10 | 11 | class Node(object): 12 | 13 | def __init__(self, data, nextnode=None): 14 | self.data = data 15 | self.nextnode = nextnode 16 | 17 | 18 | class Queue(object): 19 | 20 | def __init__(self): 21 | self.first = None 22 | self.last = None 23 | 24 | def enqueue(self, data): 25 | ''' 26 | Function to insert elements into a queue 27 | ''' 28 | node = Node(data) 29 | 30 | if self.first is None: 31 | self.first = node 32 | self.last = node 33 | 34 | else: 35 | self.last.nextnode = node 36 | self.last = self.last.nextnode 37 | 38 | 39 | def deque(self): 40 | ''' 41 | Function to delete elements from a queue 42 | ''' 43 | 44 | if self.first is None: 45 | raise ValueError("Queue is empty") 46 | 47 | print self.first.data, "is removed" 48 | 49 | self.first = self.first.nextnode 50 | 51 | def isEmpty(self): 52 | ''' 53 | Function to check if the queue is empty 54 | ''' 55 | if self.first is None: 56 | return True 57 | 58 | return False 59 | 60 | def peek(self): 61 | ''' 62 | Function to print the top of the queue 63 | ''' 64 | if self.last is None: 65 | raise ValueError("No element is there in queue") 66 | 67 | return self.last.data 68 | -------------------------------------------------------------------------------- /cracking_the_coding_interview/stack_and_queue/queue_via_stack.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF -*- 2 | 3 | ''' 4 | Problem: Implement a queue using two stacks 5 | 6 | Solution: Insert elements in one stack. When a pop request comes, pop all the elements and put them in the other stack. 7 | Then remove the top from the second stack. Now, until the second stack is empty, pop is done from second stack. 8 | When stack stack becomes empty and there is a pop request, pop all the elements from first stack and put them in second 9 | stack. 10 | ''' 11 | 12 | from stack import Stack 13 | 14 | 15 | class QueueViaStack(object): 16 | 17 | def __init__(self): 18 | self.stack1 = Stack() 19 | self.stack2 = Stack() 20 | 21 | def enqueue(self, data): 22 | ''' 23 | Function to insert elements into a queue 24 | ''' 25 | self.stack1.push(data) 26 | 27 | 28 | def deque(self): 29 | ''' 30 | Function to delete elements from a queue 31 | ''' 32 | if self.stack2.is_empty(): 33 | while(not self.stack1.is_empty()): 34 | top = self.stack1.peek() 35 | self.stack1.pop() 36 | self.stack2.push(top) 37 | 38 | top = self.stack2.peek() 39 | print top, "is deleted from queue" 40 | 41 | self.stack2.pop() 42 | 43 | def is_empty(self): 44 | ''' 45 | Function to check if the queue is empty 46 | ''' 47 | if self.stack1.is_empty() and self.stack2.is_empty(): 48 | return True 49 | 50 | return False 51 | -------------------------------------------------------------------------------- /cracking_the_coding_interview/stack_and_queue/sort_stack.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | ''' 4 | Problem: Implement a program to sort a stack such that smallest items are on the top. 5 | 6 | Solution: Insert each element in a sorted way. One stack will keep the sorted elements. When a new element is to be 7 | inserted, all the elements less than that element are popped and pushed to the second stack. Then this element is pushed 8 | and all the popped elements are pushed back. 9 | 10 | ''' 11 | 12 | from stack import Stack 13 | 14 | class SortStack(object): 15 | 16 | def __init__(self): 17 | self.stack1 = Stack() 18 | self.stack2 = Stack() 19 | 20 | def push(self, data): 21 | ''' 22 | Function to insert element in an sorted stack 23 | ''' 24 | 25 | if not self.stack1.is_empty(): 26 | while(not self.stack1.is_empty() and self.stack1.peek() < data): 27 | top = self.stack1.peek() 28 | self.stack1.pop() 29 | self.stack2.push(top) 30 | 31 | self.stack1.push(data) 32 | 33 | if not self.stack2.is_empty(): 34 | while(not self.stack2.is_empty()): 35 | top = self.stack2.peek() 36 | self.stack2.pop() 37 | self.stack1.push(top) 38 | 39 | def pop(self): 40 | ''' 41 | Function to pop element from the sorted stack 42 | ''' 43 | if self.stack1.is_empty(): 44 | raise ValueError("Stack is empty") 45 | 46 | self.stack1.pop() 47 | 48 | def is_empty(self): 49 | ''' 50 | Function to check if the sorted stack is empty 51 | ''' 52 | if self.stack1.is_empty(): 53 | return True 54 | 55 | return False 56 | -------------------------------------------------------------------------------- /cracking_the_coding_interview/stack_and_queue/stack.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | ''' 4 | Problem: Implement stack in python 5 | 6 | Stack: A stack is a data structure which stores elements in a linear fashion such that each element is added and 7 | removed from the top of the stack. 8 | 9 | ''' 10 | 11 | class Node(object): 12 | 13 | def __init__(self, data, nextnode=None): 14 | self.data = data 15 | self.nextnode = nextnode 16 | 17 | class Stack(object): 18 | 19 | def __init__(self): 20 | self.top = None 21 | 22 | def is_empty(self): 23 | ''' 24 | Function to check if the stack is empty 25 | ''' 26 | if self.top is None: 27 | return True 28 | 29 | return False 30 | 31 | def push(self, data): 32 | ''' 33 | Function to insert an element to the top of a stack 34 | ''' 35 | node = Node(data=data) 36 | 37 | if self.top is None: 38 | self.top = node 39 | 40 | else: 41 | temp = self.top 42 | self.top = node 43 | self.top.nextnode = temp 44 | 45 | def pop(self): 46 | ''' 47 | Function to delete an element from the top of the stack 48 | ''' 49 | if self.top is None: 50 | raise ValueError("Stack is empty") 51 | 52 | else: 53 | print self.top.data, "is deleted" 54 | self.top = self.top.nextnode 55 | 56 | 57 | def peek(self): 58 | ''' 59 | Function to get the top of the stack 60 | ''' 61 | if self.top is None: 62 | raise ValueError("Stack is empty") 63 | 64 | else: 65 | return (self.top.data) 66 | -------------------------------------------------------------------------------- /cracking_the_coding_interview/trees_and_graphs/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/charulagrl/data-structures-and-algorithms/79ccc0aa34d9e3eb508bfe64d1fcee9dd1ea5e98/cracking_the_coding_interview/trees_and_graphs/.DS_Store -------------------------------------------------------------------------------- /cracking_the_coding_interview/trees_and_graphs/BSTSequence.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | ''' 4 | Problem: Create all the array sequence that will result in the same binary search tree. 5 | 6 | Example: 7 | 2 8 | / \ 9 | 1 3 10 | 11 | Result: {2, 1, 3} {2, 3, 1} 12 | ''' 13 | 14 | def findAllSeq(root): 15 | ''' Function to print all the sequences of a binary search tree ''' 16 | 17 | result = [] 18 | if root is None: 19 | result.append([]) 20 | return result 21 | 22 | prefix = [] 23 | prefix.add(root.data) 24 | 25 | left = findAllSeq(root.left) 26 | right = findAllSeq(root.right) 27 | 28 | for i in range(len(left)): 29 | for j in range(len(right)): 30 | weaved = [] 31 | weavedLists(left, right, weaved, prefix) 32 | result.append(weaved) 33 | 34 | return result 35 | 36 | def weavedLists(first, second, results, prefix): 37 | if len(first) == 0 or len(second) == 0: 38 | result = prefix 39 | result.append(first) 40 | result.append(second) 41 | results.append(result) 42 | return 43 | 44 | 45 | headfirst = 46 | prefix.append(headfirst) 47 | weavedLists(first, second, results, prefix) 48 | prefix.pop() 49 | first.append(headfirst) 50 | 51 | headSecond = second 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /cracking_the_coding_interview/trees_and_graphs/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/charulagrl/data-structures-and-algorithms/79ccc0aa34d9e3eb508bfe64d1fcee9dd1ea5e98/cracking_the_coding_interview/trees_and_graphs/__init__.py -------------------------------------------------------------------------------- /cracking_the_coding_interview/trees_and_graphs/binary_search_tree.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | ''' 4 | Program to implement binary search tree 5 | 6 | ''' 7 | 8 | class Node(object): 9 | ''' 10 | Node class represents each node of the binary search tree 11 | ''' 12 | def __init__(self, data): 13 | self.data = data 14 | self.left = None 15 | self.right = None 16 | 17 | 18 | class BinarySearchTree(object): 19 | ''' 20 | Class to represent binary search tree 21 | ''' 22 | def __init__(self): 23 | self.root = None 24 | 25 | def insert(self, data, root): 26 | ''' 27 | Function to insert node in a binary search tree 28 | ''' 29 | if root is None: 30 | return Node(data) 31 | 32 | elif data <= root.data: 33 | root.left = self.insert(data, root.left) 34 | 35 | else: 36 | root.right = self.insert(data, root.right) 37 | 38 | return root 39 | 40 | def inorder(self, node): 41 | ''' 42 | Function to print the inorder traversal of the tree 43 | ''' 44 | if node is None: 45 | return 46 | 47 | self.inorder(node.left) 48 | print (node.data) 49 | self.inorder(node.right) 50 | 51 | 52 | def initialize(): 53 | ''' 54 | Function to initialize a binary search tree 55 | ''' 56 | bst = BinarySearchTree() 57 | 58 | bst.root = bst.insert(6, bst.root) 59 | bst.insert(1, bst.root) 60 | bst.insert(9, bst.root) 61 | bst.insert(3, bst.root) 62 | bst.inorder(bst.root) 63 | 64 | return bst 65 | 66 | if __name__ == "__main__": 67 | initialize() -------------------------------------------------------------------------------- /cracking_the_coding_interview/trees_and_graphs/binary_tree.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | ''' 4 | Class to represent a binary tree node 5 | ''' 6 | 7 | class BinaryTreeNode(object): 8 | 9 | def __init__(self, data): 10 | self.data = data 11 | self.left = None 12 | self.right = None 13 | 14 | def add_left(self, left): 15 | ''' 16 | Function to add node at the left of the current node 17 | ''' 18 | self.left = left 19 | 20 | def add_right(self, right): 21 | ''' 22 | Function to add node at the right of the current node 23 | ''' 24 | self.right = right 25 | 26 | def __str__(self): 27 | text = str(self.data) 28 | text += ":" 29 | 30 | child = {} 31 | if self.left: 32 | child["left"] = str(self.left) 33 | 34 | if self.right: 35 | child["right"] = str(self.right) 36 | 37 | text += str(child) 38 | return text 39 | 40 | def initialize(): 41 | root = BinaryTreeNode(5) 42 | 43 | # Initialize the child nodes 44 | child_1 = BinaryTreeNode(4) 45 | child_2 = BinaryTreeNode(6) 46 | child_child_1 = BinaryTreeNode(3) 47 | child_child_2 = BinaryTreeNode(1) 48 | child_child_3 = BinaryTreeNode(13) 49 | child_child_4 = BinaryTreeNode(12) 50 | 51 | # Connect the child nodes to the parent node 52 | child_1.add_left(child_child_1) 53 | child_1.add_right(child_child_2) 54 | child_2.add_left(child_child_3) 55 | child_2.add_right(child_child_4) 56 | root.add_left(child_1) 57 | root.add_right(child_2) 58 | 59 | print root 60 | return root 61 | 62 | if __name__ == "__main__": 63 | initialize() -------------------------------------------------------------------------------- /cracking_the_coding_interview/trees_and_graphs/check_balanced_tree.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | ''' 4 | Problem: Check if a binary tree is balanced. 5 | Balanced tree is defined to be a tree such that the heights of the two subtree of any node never 6 | differ by more than one. 7 | 8 | Solution: At each node calculate the difference of height of left subtree and right subtree and if the difference is 9 | greater than one, returns False. Also checks the same for left node and right node. 10 | ''' 11 | import binary_tree 12 | 13 | def calculate_height(root): 14 | ''' Function to calculate the height of the subtree''' 15 | if root is None: 16 | return 0 17 | 18 | else: 19 | return max(calculate_height(root.left), calculate_height(root.right)) + 1 20 | 21 | def bruteforce_check_balanced(root): 22 | ''' Function to check if the tree is balanced''' 23 | if root is None: 24 | return True 25 | 26 | left_height = calculate_height(root.left) 27 | right_height = calculate_height(root.right) 28 | 29 | difference = abs(left_height - right_height) 30 | 31 | if difference > 1: 32 | return False 33 | 34 | return bruteforce_check_balanced(root.left) and bruteforce_check_balanced(root.right) 35 | 36 | def main_function(): 37 | root = binary_tree.initialize() 38 | print calculate_height(root) 39 | 40 | print bruteforce_check_balanced(root) 41 | 42 | if __name__ == "__main__": 43 | main_function() 44 | -------------------------------------------------------------------------------- /cracking_the_coding_interview/trees_and_graphs/first_common_ancestor.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | ''' 4 | Problem: If two nodes are given, find the first commom ancestor of two nodes. 5 | 6 | Solution: At any node, check if the two nodes lie only in the left subtree or right subtree. If they lie on one side, 7 | then call the recursion on that side. Otherwise return the node. 8 | 9 | 10 | ''' 11 | 12 | def common_ancestor(root, p, q): 13 | ''' Function to find the first common ancestor of two nodes ''' 14 | 15 | if not root: 16 | return None 17 | 18 | # If the p or q is equal to the root node, then root is the first ancestor 19 | if root == p or root == q: 20 | return root 21 | 22 | left = common_ancestor(root.left, p, q) 23 | right = common_ancestor(root.right, p, q) 24 | 25 | # If two nodes lie on both left and right subtree 26 | if left is not None and right is not None: 27 | return root 28 | 29 | # If both nodes lie in the left subtree 30 | if right is None: 31 | return left 32 | # If both nodes lie on the right subtree 33 | if left is None: 34 | return right 35 | 36 | def main_function(): 37 | ''' Function to initialize the binary tree''' 38 | import binary_tree 39 | root = binary_tree.initialize() 40 | node = common_ancestor(root, root.left, root.right.right) 41 | print node.data 42 | 43 | if __name__ == "__main__": 44 | main_function() 45 | -------------------------------------------------------------------------------- /cracking_the_coding_interview/trees_and_graphs/minimal_tree.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | ''' 4 | Problem: Given a sorted array with unique integer elements, write a algorithm to create a binary tree with 5 | minimum height. 6 | 7 | Solution: Our main objective is to divie node such that left subtree and right subtree have equal number of nodes. 8 | Hence, the middle of each subsection becomes the root of the node. Left half of the array becomes the left subtree and 9 | right half of the array becomes the right subtree. 10 | ''' 11 | 12 | import binary_tree 13 | 14 | def minimal_tree(sorted_array, start, end): 15 | 16 | if end < start: 17 | return None 18 | 19 | index = start + (end - start)/2 20 | 21 | node = binary_tree.BinaryTreeNode(sorted_array[index]) 22 | node.left = minimal_tree(sorted_array, start, index-1) 23 | node.right = minimal_tree(sorted_array, index+1, end) 24 | 25 | return node 26 | 27 | def minimal_tree_helper(): 28 | sorted_array = [3, 5, 7, 8, 10, 13, 15] 29 | 30 | root = minimal_tree(sorted_array, 0, len(sorted_array)-1) 31 | print (root) 32 | 33 | if __name__ == "__main__": 34 | minimal_tree_helper() 35 | -------------------------------------------------------------------------------- /cracking_the_coding_interview/trees_and_graphs/path_between_two_nodes.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | ''' 3 | Problem: Given a graph, find the path between two nodes 4 | 5 | Solution: We will traverse the path using depth first search and append the nodes visited in the path. 6 | ''' 7 | 8 | import graph 9 | 10 | def depth_first_search_helper(g, src, dest, visited): 11 | ''' 12 | Find the path between two nodes using depth first search approach 13 | ''' 14 | if src == dest: 15 | print dest.name 16 | return 17 | 18 | visited[src] = 1 19 | print src.name 20 | 21 | for edge in g.adjacency_dict[src]: 22 | if visited[edge.dest] is not True: 23 | depth_first_search_helper(g, edge.dest, dest, visited) 24 | 25 | def depth_first_search(): 26 | ''' 27 | Create a graph and call the depth first search function 28 | ''' 29 | 30 | # Initialize a graph 31 | g = graph.AdjacencyListGraph() 32 | 33 | a = g.create_vertex("a") 34 | b = g.create_vertex("b") 35 | c = g.create_vertex("c") 36 | d = g.create_vertex("d") 37 | g.add_edge(a, b, directed=True) 38 | g.add_edge(b, c, directed=True) 39 | g.add_edge(c, d, directed=True) 40 | g.add_edge(d, b, directed=True) 41 | 42 | print (g) 43 | 44 | # Create a dict which keeps the node that have been visited 45 | visited = {} 46 | for key in g.adjacency_dict: 47 | visited[key] = False 48 | 49 | depth_first_search_helper(g, b, d, visited) 50 | -------------------------------------------------------------------------------- /cracking_the_coding_interview/trees_and_graphs/queue.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | ''' 4 | Class to implement queue in Python 5 | 6 | Queue: A queue stores elements in a linear fashion such that they follow first-in-first-out(FIFO) order. The elements 7 | are added to the top of the queue and removed from the beginning of the queue. 8 | 9 | ''' 10 | 11 | class Node(object): 12 | 13 | def __init__(self, data, left=None, right=None): 14 | self.data = data 15 | self.left = left 16 | self.right = right 17 | self.nextnode = None 18 | 19 | 20 | 21 | class Queue(object): 22 | 23 | def __init__(self): 24 | self.first = None 25 | self.last = None 26 | 27 | def enqueue(self, data): 28 | ''' 29 | Function to insert elements into a queue 30 | ''' 31 | # TODO: This should be dynamic in the sense that it can take any class 32 | node = Node(data) 33 | 34 | if self.first is None: 35 | self.first = node 36 | self.last = node 37 | 38 | else: 39 | self.last.nextnode = node 40 | self.last = self.last.nextnode 41 | 42 | 43 | def deque(self): 44 | ''' 45 | Function to delete elements from a queue 46 | ''' 47 | if self.first is None: 48 | raise ValueError("Queue is empty") 49 | 50 | # print self.first.data, "is removed" 51 | temp = self.first 52 | self.first = self.first.nextnode 53 | 54 | return temp 55 | 56 | def isEmpty(self): 57 | ''' 58 | Function to check if the queue is empty 59 | ''' 60 | if self.first is None: 61 | return True 62 | 63 | return False 64 | 65 | def peek(self): 66 | ''' 67 | Function to print the top of the queue 68 | ''' 69 | if self.last is None: 70 | raise ValueError("No element is there in queue") 71 | 72 | return self.last.data 73 | -------------------------------------------------------------------------------- /cracking_the_coding_interview/trees_and_graphs/tree.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | class TreeNode(object): 4 | ''' 5 | Class to represent a tree node 6 | ''' 7 | def __init__(self, data): 8 | self.data = data 9 | self.children = [] 10 | 11 | def add(self, child): 12 | ''' 13 | Add the child node 14 | ''' 15 | self.children.append(child) 16 | 17 | def __str__(self): 18 | text = str(self.data) 19 | text += ': {' + ', '.join([str(child) for child in self.children]) + "} " 20 | 21 | return text 22 | 23 | def initialize(): 24 | tree = TreeNode(5) 25 | 26 | # Initialize the child nodes 27 | child_1 = TreeNode(4) 28 | child_2 = TreeNode(6) 29 | child_3 = TreeNode(8) 30 | child_child = TreeNode(3) 31 | 32 | # Connect the child nodes to the parent node 33 | child_1.add(child_child) 34 | tree.add(child_1) 35 | tree.add(child_2) 36 | tree.add(child_3) 37 | 38 | print(tree) 39 | 40 | if __name__ == "__main__": 41 | initialize() 42 | -------------------------------------------------------------------------------- /data-structure-questions/array/bitonic_search.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | # Program to search a element in bitonic array 4 | 5 | def bitonic_search(arr, low, high): 6 | 7 | if not arr: 8 | return "List is empty" 9 | 10 | if low == high: 11 | return arr[high] 12 | 13 | mid = (low + high)/2 14 | 15 | if arr[mid] < arr[mid+1]: 16 | return bitonic_search(arr, mid+1, high) 17 | elif arr[mid] < arr[mid-1]: 18 | return bitonic_search(arr, low, mid-1) 19 | else: 20 | return arr[mid] 21 | 22 | 23 | arr = [3, 4, 5, 7, 8, 13, 56, 44, 32, 9, 1] 24 | 25 | print bitonic_search(arr, 0, len(arr)-1) -------------------------------------------------------------------------------- /data-structure-questions/array/count_duplicate.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | # Program to find the number of duplicate elements in an array 4 | 5 | def count_duplicate(arr): 6 | records = {} 7 | 8 | for a in arr: 9 | if a not in records.keys(): 10 | records[a] = 0 11 | elif records[a] == 0: 12 | records[a] = 1 13 | 14 | count = 0 15 | for record in records.values(): 16 | if record == 1: 17 | count += 1 18 | 19 | return count 20 | 21 | 22 | print count_duplicate("ababbdjhh") 23 | -------------------------------------------------------------------------------- /data-structure-questions/array/first_and_last_occurence_in_sorted_array.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | # Program to find the first and last occurence of an element in an array 4 | 5 | def find_first(arr, element): 6 | 7 | for i in range(len(arr)): 8 | if arr[i] == element: 9 | return i 10 | 11 | return -1 12 | 13 | def find_last(arr, element): 14 | 15 | index = -1 16 | for i in range(len(arr)): 17 | if arr[i] == element: 18 | if i+1 < len(arr) and arr[i+1] == element: 19 | index = i+1 20 | else: 21 | index = i 22 | break 23 | 24 | return index 25 | 26 | arr = [1, 2, 3, 3, 3, 3, 4, 5, 5, 5, 6, 7, 7] 27 | print find_first(arr, 3) 28 | print find_last(arr, 7) 29 | 30 | -------------------------------------------------------------------------------- /data-structure-questions/array/majority_element.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | # Find out the majority element in the array using Moore's algorithm 4 | 5 | # A majority element in an array A[] of size n is an element that appears more than n/2 times (and hence there is at most one such element). 6 | 7 | def majority_element(ar): 8 | 9 | count = 1 10 | maj_index = 0 11 | 12 | for i in range(len(ar)): 13 | if count == 0: 14 | maj_index = i 15 | count = 1 16 | if ar[i] == ar[maj_index]: 17 | count += 1 18 | else: 19 | count -= 1 20 | 21 | return ar[maj_index] 22 | 23 | print majority_element([2, 2, 3, 5, 2, 2, 6]) -------------------------------------------------------------------------------- /data-structure-questions/binary_search_tree/bst.py: -------------------------------------------------------------------------------- 1 | class Node(object): 2 | 3 | def __init__(self, data): 4 | self.data = data 5 | self.left = None 6 | self.right = None 7 | 8 | 9 | class Tree(object): 10 | 11 | def __init__(self): 12 | self.root = None 13 | 14 | def insert(self, node, data): 15 | if node is None: 16 | return Node(data) 17 | 18 | if data > node.data: 19 | node.right = self.insert(node.right, data) 20 | else: 21 | node.left = self.insert(node.left, data) 22 | 23 | return node 24 | 25 | def inorder(self, root): 26 | if root is not None: 27 | self.inorder(root.left) 28 | print root.data 29 | self.inorder(root.right) 30 | 31 | def preorder(self, root): 32 | if root is not None: 33 | print root.data 34 | self.preorder(root.left) 35 | self.preorder(root.right) 36 | 37 | def postorder(self, root): 38 | if root is not None: 39 | self.postorder(root.left) 40 | self.postorder(root.right) 41 | print root.data 42 | 43 | def height(self, root): 44 | if root is None: 45 | return 0 46 | 47 | else: 48 | l = self.height(root.left) 49 | r = self.height(root.right) 50 | 51 | if l > r: 52 | return l + 1 53 | else: 54 | return r + 1 55 | 56 | def search(self, node, data): 57 | 58 | if node == None: 59 | return None 60 | 61 | if node.data == data: 62 | return node 63 | 64 | if node.data < data: 65 | self.search(node.right, data) 66 | else: 67 | self.search(node.left, data) 68 | 69 | return node 70 | -------------------------------------------------------------------------------- /data-structure-questions/binary_search_tree/lowest_common_ancestor_bst.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | # Program to find lowest common ancestor of two nodes 4 | 5 | import bst 6 | 7 | def lowest_common_ancestor(root, node1, node2): 8 | 9 | if root == None: 10 | return 11 | 12 | if root.data > node1 and root.data > node2: 13 | lowest_common_ancestor(root.left, node1, node2) 14 | 15 | if root.data < node1 and root.data < node2: 16 | lowest_common_ancestor(root.right, node1, node2) 17 | 18 | return root.data 19 | 20 | if __name__=="__main__": 21 | 22 | tree = bst.Tree() 23 | elements = [3, 5, 9, 8, 1, 7] 24 | 25 | root = tree.insert(tree.root, 6) 26 | 27 | for element in elements: 28 | tree.insert(root, element) 29 | 30 | # tree.inorder(root) 31 | 32 | print lowest_common_ancestor(root, 5, 9) 33 | -------------------------------------------------------------------------------- /data-structure-questions/binary_tree/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/charulagrl/data-structures-and-algorithms/79ccc0aa34d9e3eb508bfe64d1fcee9dd1ea5e98/data-structure-questions/binary_tree/__init__.py -------------------------------------------------------------------------------- /data-structure-questions/binary_tree/binary_tree.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | # Program to construct Binary Tree 4 | 5 | class Node(object): 6 | 7 | def __init__(self, data): 8 | 9 | self.data = data 10 | self.left = None 11 | self.right = None 12 | 13 | class Tree(object): 14 | 15 | def __init__(self, root): 16 | 17 | self.root = root 18 | 19 | def inorder(self, root): 20 | if root is not None: 21 | self.inorder(root.left) 22 | print root.data 23 | self.inorder(root.right) 24 | 25 | def height(self, root): 26 | 27 | if root is None: 28 | return 0 29 | 30 | else: 31 | lh = self.height(root.left) 32 | rh = self.height(root.right) 33 | 34 | if lh < rh: 35 | return rh + 1 36 | else: 37 | return lh + 1 38 | 39 | 40 | def construct_binary_tree(): 41 | 42 | # initialize root node of a tree 43 | root = Node(1) 44 | 45 | # initialize a binary tree 46 | tree = Tree(root) 47 | 48 | root.left = Node(2) 49 | root.right = Node(3) 50 | root.left.left = Node(4) 51 | root.left.right = Node(5) 52 | 53 | return tree 54 | -------------------------------------------------------------------------------- /data-structure-questions/binary_tree/binary_tree_diameter.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | # Program to find the diameter of a binary tree 4 | # i.e. number of nodes in the longest path between two leaves in the tree 5 | 6 | import binary_tree 7 | 8 | def diameter(tree, node): 9 | 10 | if node is None: 11 | return 0 12 | 13 | lh = tree.height(node.left) 14 | rh = tree.height(node.right) 15 | 16 | return max(lh+rh+1, max(diameter(tree, node.left), diameter(tree, node.right))) 17 | 18 | 19 | if __name__=="__main__": 20 | tree = binary_tree.construct_binary_tree() 21 | 22 | print diameter(tree, tree.root) -------------------------------------------------------------------------------- /data-structure-questions/binary_tree/binary_tree_to_doubly_link_list.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | # Convert a given Binary Tree to Doubly Linked List 4 | 5 | import binary_tree 6 | 7 | def convert_to_doubly_linked_list(root): 8 | if not root: 9 | return 10 | 11 | if root.left: 12 | left = convert_to_doubly_linked_list(root.left) 13 | 14 | while left.right: 15 | left = left.right 16 | 17 | left.right = root 18 | root.left = left 19 | 20 | if root.right: 21 | right = convert_to_doubly_linked_list(root.right) 22 | 23 | while right.left: 24 | right = right.left 25 | 26 | root.right = right 27 | right.left = root 28 | 29 | return root 30 | 31 | def print_list(root): 32 | if not root: 33 | return 34 | 35 | while root.left: 36 | root = root.left 37 | 38 | head = root 39 | 40 | while head: 41 | print head.data 42 | head = head.right 43 | 44 | tree = binary_tree.construct_binary_tree() 45 | root = convert_to_doubly_linked_list(tree.root) 46 | print_list(root) -------------------------------------------------------------------------------- /data-structure-questions/binary_tree/boundary_traversal.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | # Boundary Traversal 4 | 5 | ''' 6 | Given a binary tree, print boundary nodes of the binary tree Anti-Clockwise starting from the root. 7 | ''' 8 | import binary_tree 9 | 10 | def printBoundaryLeft(root): 11 | '''Print left boundary of the tree''' 12 | if not root: 13 | return 14 | 15 | '''We won't do anything if it's a leaf node to avoid duplicates''' 16 | if root.left: 17 | print root.data 18 | printBoundaryLeft(root.left) 19 | elif root.right: 20 | print root.data 21 | printBoundaryLeft(root.right) 22 | 23 | def printLeaves(root): 24 | '''Print leaf nodes of a binary tree''' 25 | if not root: 26 | return 27 | 28 | printLeaves(root.left) 29 | 30 | if not root.left and not root.right: 31 | print root.data 32 | 33 | printLeaves(root.right) 34 | 35 | def printBoundaryRight(root): 36 | '''Print right boundary of the binary tree''' 37 | if not root: 38 | return 39 | '''To ensure bottom up order, first call for right subtree, then print this node''' 40 | if root.right: 41 | printBoundaryRight(root.right) 42 | print root.data 43 | elif root.left: 44 | printBoundaryRight(root.left) 45 | print root.data 46 | 47 | def printBoundary(root): 48 | '''Print the boundary traversal of a tree''' 49 | if not root: 50 | return 51 | 52 | print root.data 53 | printBoundaryLeft(root.left) 54 | 55 | printLeaves(root.left) 56 | printLeaves(root.right) 57 | 58 | printBoundaryRight(root.right) 59 | 60 | if __name__ == "__main__": 61 | tree = binary_tree.construct_binary_tree() 62 | printBoundary(tree.root) 63 | -------------------------------------------------------------------------------- /data-structure-questions/binary_tree/check_if_all_leaves_are_at_same_level.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | # Check if all the leaves are at same level 4 | 5 | import binary_tree 6 | 7 | 8 | def check_leaves(root, level, given_level): 9 | if root is None: 10 | return True 11 | 12 | elif root.left is None and root.right is None: 13 | if given_level[0] == 0: 14 | given_level[0] = level 15 | return True 16 | else: 17 | return (level == given_level[0]) 18 | 19 | else: 20 | return check_leaves(root.left, level+1, given_level) and check_leaves(root.right, level+1, given_level) 21 | 22 | 23 | if __name__=="__main__": 24 | tree = binary_tree.construct_binary_tree() 25 | 26 | print check_leaves(tree.root, 1, [0]) -------------------------------------------------------------------------------- /data-structure-questions/binary_tree/check_if_bst.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | # Program to check if the binary tree is a binary search tree(BST) or not 4 | import sys 5 | import binary_tree 6 | 7 | '''This is easy but wrong approach to solve this. This just check an element with its child nodes. if they satify the bst condition, it returns true. But for a bst, every node to the left of subtree is lesser than node data and every node to teh right of subtree has greater value than node data''' 8 | 9 | # def check_if_bst(node): 10 | 11 | # if node == None: 12 | # return True 13 | 14 | # # false if left is greater than node 15 | # if node.left and node.left.data > node.data: 16 | # return False 17 | 18 | # # false if right is less than node 19 | # if node.right and node.right.data < node.data: 20 | # return False 21 | 22 | # # false if any of the sub tree is not bst 23 | # if not check_if_bst(node.left) or not check_if_bst(node.right): 24 | # return False 25 | 26 | # # else true 27 | # return True 28 | 29 | def check_if_bst(node): 30 | 31 | return check_if_bst_util(node, -sys.maxint, sys.maxint) 32 | 33 | def check_if_bst_util(node, min_val, max_val): 34 | 35 | if node == None: 36 | return True 37 | 38 | # false if node is lesser than min value or greater than max value 39 | if node.data < min_val or node.data > max_val: 40 | return False 41 | 42 | if check_if_bst_util(node.left, min_val, node.data) and check_if_bst_util(node.right, node.data, max_val): 43 | return True 44 | else: 45 | return False 46 | 47 | 48 | if __name__=="__main__": 49 | tree = binary_tree.construct_binary_tree() 50 | 51 | print check_if_bst(tree.root) -------------------------------------------------------------------------------- /data-structure-questions/binary_tree/check_if_child_sum_property.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | ''' 4 | Program to check if child sum property holds in the binary tree. For every node, data value must be equal to 5 | sum of data values in left and right children. 6 | ''' 7 | 8 | import binary_tree 9 | 10 | def child_sum_property(root): 11 | '''Return true if a tree holds a child sum property''' 12 | if root is None: 13 | return True 14 | 15 | else: 16 | if root.left is None and root.right is None: 17 | return True 18 | else: 19 | left = root.left.data if root.left is not None else 0 20 | right = root.right.data if root.right is not None else 0 21 | 22 | if root.data != left + right: 23 | return False 24 | else: 25 | return child_sum_property(root.left) and child_sum_property(root.right) 26 | 27 | if __name__=="__main__": 28 | tree = binary_tree.construct_binary_tree() 29 | print child_sum_property(tree.root) 30 | -------------------------------------------------------------------------------- /data-structure-questions/binary_tree/check_if_tree_is_complete_tree.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | # Program to check if a tree is complete or not 4 | ''' 5 | A complete binary tree is a binary tree in which every level, except possibly the last, is completely 6 | filled, and all nodes are as far left as possible. 7 | ''' 8 | 9 | import binary_tree 10 | import Queue 11 | 12 | def check_tree(root): 13 | '''Check if binary tree is complete''' 14 | if not root: 15 | return 16 | 17 | queue = Queue.Queue() 18 | flag = False 19 | queue.put(root) 20 | while not queue.empty(): 21 | 22 | s = queue.get() 23 | 24 | if s.left: 25 | if flag: 26 | return False 27 | # Enqueue left child 28 | queue.append(s.left) 29 | # If this a non-full node, set the flag as true 30 | else: 31 | flag = True 32 | 33 | if s.right: 34 | if flag: 35 | return False 36 | # Enqueue right child 37 | queue.append(s.right) 38 | else: 39 | flag = False 40 | 41 | return True 42 | 43 | if __name__ == "__main__": 44 | tree = binary_tree.construct_binary_tree() 45 | print check_tree(tree.root) 46 | -------------------------------------------------------------------------------- /data-structure-questions/binary_tree/check_if_tree_is_subtree_of_another.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | import binary_tree 4 | 5 | # Program to check if a tree is subtree of another tree 6 | 7 | def check_for_rest_of_tree(tree_a, tree_b): 8 | '''Check if two trees are identical trees''' 9 | if not tree_a and not tree_b: 10 | return True 11 | 12 | elif not tree_b: 13 | return True 14 | 15 | else: 16 | if tree_a.data == tree_b.data and check_for_rest_of_tree(tree_a.left, tree_b.left)\ 17 | and check_for_rest_of_tree(tree_a.right, tree_b.right): 18 | return True 19 | 20 | else: 21 | return False 22 | 23 | def check_subtree(root1, root2): 24 | '''Check if tree2 is a subtree of tree 1''' 25 | if root1 is None: 26 | return False 27 | 28 | if root2 is None: 29 | return True 30 | 31 | else: 32 | 33 | if root1.data == root2.data: 34 | if check_for_rest_of_tree(root1, root2): 35 | return True 36 | 37 | return check_subtree(root.left, root1) 38 | return check_subtree(root.right, root2) 39 | 40 | if __name__=='__main__': 41 | tree = binary_tree.construct_binary_tree() 42 | 43 | # initialize root node of a tree 44 | root = binary_tree.Node(10) 45 | 46 | # initialize a binary subtree 47 | tree1 = binary_tree.Tree(root) 48 | 49 | root.left = binary_tree.Node(4) 50 | root.right = binary_tree.Node(6) 51 | root.left.right = binary_tree.Node(3) 52 | 53 | print "tree 1" 54 | tree.inorder(tree.root) 55 | print "tree 2" 56 | tree1.inorder(tree1.root) 57 | 58 | print check_subtree(tree.root, tree1.root) 59 | -------------------------------------------------------------------------------- /data-structure-questions/binary_tree/check_if_tree_is_sumtree.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | ''' 4 | Check if a given tree is a sumTree i.e. A SumTree is a Binary Tree where the value of a node is equal 5 | to sum of the nodes present in its left subtree and right subtree. 6 | ''' 7 | import binary_tree 8 | 9 | def check_sumtree(root): 10 | if not root or (not root.left and not root.right): 11 | return True 12 | 13 | else: 14 | 15 | if check_sumtree(root.left) and check_sumtree(root.right): 16 | 17 | if not root.left: 18 | ls = 0 19 | elif not root.left.left and not root.left.right: 20 | ls = root.left.data 21 | else: 22 | ls = 2 * root.left.data 23 | 24 | if not root.right: 25 | rs = 0 26 | elif not root.right.left and not root.right.right: 27 | rs = root.right.data 28 | else: 29 | rs = 2 * root.right.data 30 | 31 | if root.data == (ls+rs): 32 | return True 33 | 34 | return False 35 | 36 | if __name__ == "__main__": 37 | root = binary_tree.Node(26) 38 | root.left = binary_tree.Node(10) 39 | root.right = binary_tree.Node(3) 40 | root.left.left = binary_tree.Node(4) 41 | root.left.right = binary_tree.Node(6) 42 | root.right.right = binary_tree.Node(3) 43 | tree = binary_tree.Tree(root) 44 | tree.inorder(root) 45 | print check_sumtree(tree.root) 46 | -------------------------------------------------------------------------------- /data-structure-questions/binary_tree/connect_nodes.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | # Program to connect nodes which are at the same level 4 | 5 | import binary_tree 6 | 7 | class Node(object): 8 | def __init__(self, data): 9 | self.data = data 10 | self.left = None 11 | self.right = None 12 | self.next = None 13 | 14 | def connect_nodes(root): 15 | '''Connect nodes in a tree considering that it is a complete binary tree''' 16 | if root == None: 17 | return 18 | 19 | '''Before setting next of left and right children, set next of children of other 20 | nodes at same level (because we can access children of other nodes using p's next only)''' 21 | if root.next != None: 22 | connect_nodes(root.next) 23 | 24 | if root.left: 25 | if root.right: 26 | root.left.next = root.right 27 | root.right.next = getNextRight(root) 28 | else: 29 | root.left.next = getNextRight(root) 30 | 31 | if root.right: 32 | if root.next: 33 | root.right.next = root.next.left 34 | else: 35 | root.right.next = None 36 | 37 | connect_nodes(root.right) 38 | connect_nodes(root.left) 39 | 40 | 41 | def inorder(root): 42 | '''Print inorder traversal of a tree''' 43 | if root is not None: 44 | inorder(root.left) 45 | print root.data 46 | if root.next: 47 | print "Next " + str(root.next.data) 48 | inorder(root.right) 49 | 50 | 51 | def construct_binary_tree(): 52 | # initialize root node of a tree 53 | root = Node(1) 54 | 55 | # initialize a binary tree 56 | tree = binary_tree.Tree(root) 57 | 58 | root.left = Node(2) 59 | root.right = Node(3) 60 | root.right.left = Node(4) 61 | root.right.right = Node(5) 62 | root.right.left.right = Node(6) 63 | 64 | return tree 65 | 66 | if __name__=="__main__": 67 | tree = construct_binary_tree() 68 | connect_nodes(tree.root) 69 | inorder(tree.root) 70 | -------------------------------------------------------------------------------- /data-structure-questions/binary_tree/construct_binary_tree_from_linked_list.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | # Construct a complete binary tree from its linked list representation 4 | 5 | import binary_tree 6 | from linked_list import linked_list 7 | 8 | def construct_tree(head, index): 9 | '''Construct tree from linked list and return root of the tree''' 10 | current = head 11 | count = 0 12 | while (count < index and current): 13 | current = current.nextnode 14 | count = count + 1 15 | 16 | if current is None: 17 | return 18 | 19 | root = binary_tree.Node(current.data) 20 | 21 | root.left = construct_tree(head, 2*index+1) 22 | root.right = construct_tree(head, 2*index+2) 23 | 24 | return root 25 | 26 | if __name__ == "__main__": 27 | # Create a linked list 28 | head = linked_list.Node(10) 29 | head.nextnode = linked_list.Node(12) 30 | head.nextnode.nextnode = linked_list.Node(15) 31 | head.nextnode.nextnode.nextnode = linked_list.Node(25) 32 | head.nextnode.nextnode.nextnode.nextnode = linked_list.Node(30) 33 | head.nextnode.nextnode.nextnode.nextnode.nextnode = linked_list.Node(36) 34 | 35 | root = construct_tree(head, 0) 36 | tree = binary_tree.Tree(root) 37 | tree.inorder(tree.root) 38 | -------------------------------------------------------------------------------- /data-structure-questions/binary_tree/construct_special_tree_from_inorder.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | ''' 4 | Given Inorder Traversal of a Special Binary Tree in which key of every node is greater than keys in 5 | left and right children, construct the Binary Tree and return root. 6 | ''' 7 | import binary_tree 8 | 9 | def special_tree(sequence, left, right): 10 | if left > right: 11 | return 12 | 13 | else: 14 | index = left 15 | # Find the highest element in the increasing sequence 16 | while (index < right and sequence[index] < sequence[index+1]): 17 | index = index + 1 18 | 19 | root = binary_tree.Node(sequence[index]) 20 | 21 | root.left = special_tree(sequence, left, index-1) 22 | root.right = special_tree(sequence, index+1, right) 23 | 24 | return root 25 | 26 | if __name__ == "__main__": 27 | inorder = [1, 5, 10, 40, 30, 15, 28, 20] 28 | 29 | root = special_tree(inorder, 0, len(inorder)-1) 30 | tree = binary_tree.Tree(root) 31 | tree.inorder(tree.root) 32 | -------------------------------------------------------------------------------- /data-structure-questions/binary_tree/construct_special_tree_from_preorder.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | # Program to construct a special tree from preorder traversal 4 | 5 | ''' 6 | Given an array ‘pre[]’ that represents Preorder traversal of a spacial binary tree where 7 | every node has either 0 or 2 children. One more array ‘preLN[]’ is given which has only two 8 | possible values ‘L’ and ‘N’. The value ‘L’ in ‘preLN[]’ indicates that the corresponding node 9 | in Binary Tree is a leaf node and value ‘N’ indicates that the corresponding node is non-leaf node. 10 | ''' 11 | import binary_tree 12 | 13 | index = -1 14 | def construct_tree(pre, preLN): 15 | global index 16 | index += 1 17 | 18 | if len(pre) == 0: 19 | return 20 | 21 | root = binary_tree.Node(pre[index]) 22 | 23 | if preLN[index] == "N": 24 | root.left = construct_tree(pre, preLN) 25 | root.right = construct_tree(pre, preLN) 26 | 27 | return root 28 | 29 | pre = [10, 30, 20, 5, 15] 30 | preLN = ['N', 'N', 'L', 'L', 'L'] 31 | 32 | root = construct_tree(pre, preLN) 33 | tree = binary_tree.Tree(root) 34 | tree.inorder(tree.root) -------------------------------------------------------------------------------- /data-structure-questions/binary_tree/construct_tree_from_inorder_preorder.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | # Program to construct binary tree from its inorder and preorder traversal 4 | 5 | import binary_tree 6 | 7 | preindex = 0 8 | 9 | def construct_tree(preorder, inorder, start, end): 10 | global preindex 11 | 12 | if start > end: 13 | return 14 | 15 | root = binary_tree.Node(preorder[preindex]) 16 | preindex += 1 17 | 18 | if start == end: 19 | return root 20 | 21 | index = inorder.index(root.data) 22 | 23 | if index >= 0: 24 | root.left = construct_tree(preorder, inorder, start, index-1) 25 | root.right = construct_tree(preorder, inorder, index+1, end) 26 | 27 | return root 28 | 29 | if __name__=="__main__": 30 | 31 | root = construct_tree([1, 2, 4, 5, 3], [4, 2, 5, 1, 3], 0, 4) 32 | 33 | tree = binary_tree.Tree(root) 34 | 35 | tree.inorder(root) 36 | -------------------------------------------------------------------------------- /data-structure-questions/binary_tree/construct_tree_from_preorder.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | # Program to construct a tree from its preorder traversal 4 | import binary_tree 5 | import sys 6 | 7 | preIndex = 0 8 | 9 | def construct_tree(preorder, maximum, minimum, key): 10 | 11 | global preIndex 12 | 13 | if preIndex > len(preorder): 14 | return 15 | 16 | if key > minimum and key < maximum: 17 | 18 | root = binary_tree.Node(key) 19 | 20 | preIndex += 1 21 | 22 | if preIndex < len(preorder): 23 | 24 | root.left = construct_tree(preorder, root.data, minimum, preorder[preIndex]) 25 | 26 | root.right = construct_tree(preorder, maximum, root.data, preorder[preIndex]) 27 | 28 | return root 29 | 30 | if __name__=="__main__": 31 | 32 | root = construct_tree([3, 2, 1, 5, 4, 6], sys.max_int, -sys.max_int, 3) 33 | 34 | tree = binary_tree.Tree(root) 35 | 36 | tree.inorder(root) -------------------------------------------------------------------------------- /data-structure-questions/binary_tree/construct_tree_from_preorder_postorder.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | ''' 4 | Construct tree from its preorder and postorder traversal 5 | A Full Binary Tree is a binary tree where every node has either 0 or 2 children 6 | ''' 7 | 8 | import binary_tree 9 | 10 | index_pre = 0 11 | def construct_tree(preorder, postorder, st_in, end_in): 12 | global index_pre 13 | 14 | if index_pre >= len(preorder) or st_in > end_in: 15 | return 16 | 17 | else: 18 | root = binary_tree.Node(preorder[index_pre]) 19 | index_pre += 1 20 | 21 | if st_in == end_in: 22 | return root 23 | 24 | # Look for the preorder value in postorder, all the elements before index_pre will be in left subtree 25 | index = postorder.index(preorder[index_pre]) 26 | 27 | if index <= len(postorder): 28 | root.left = construct_tree(preorder, postorder, st_in, index) 29 | 30 | root.right = construct_tree(preorder, postorder, index+1, end_in-1) 31 | 32 | return root 33 | 34 | 35 | if __name__ == "__main__": 36 | preorder = [1, 2, 4, 8, 9, 5, 3, 6, 7] 37 | postorder = [8, 9, 4, 5, 2, 6, 7, 3, 1] 38 | 39 | root = construct_tree(preorder, postorder, 0, len(postorder)-1) 40 | print "Inorder traversal of constructed tree" 41 | tree = binary_tree.Tree(root) 42 | tree.inorder(root) 43 | -------------------------------------------------------------------------------- /data-structure-questions/binary_tree/construct_tree_from_sorted_array.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | # Program to construct Balanced BST from a sorted list 4 | 5 | import binary_tree 6 | 7 | def construct_tree(list, s_in, e_in): 8 | 9 | if s_in > e_in: 10 | return 11 | 12 | index = (s_in+e_in)/2 13 | 14 | if index < len(list): 15 | root = binary_tree.Node(list[index]) 16 | 17 | root.left = construct_tree(list, s_in, index-1) 18 | root.right = construct_tree(list, index+1, e_in) 19 | 20 | return root 21 | 22 | if __name__=='__main__': 23 | # tree = binary_tree.construct_binary_tree() 24 | root = construct_tree([1,2,3,4,5,6,7], 0, 7) 25 | tree = binary_tree.Tree(root) 26 | tree.inorder(root) -------------------------------------------------------------------------------- /data-structure-questions/binary_tree/convert_to_children_sum_property.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | # Program to convert a tree such that it follows child sum property 4 | 5 | import binary_tree 6 | 7 | def increment(node, diff): 8 | '''Increment one child nodes of a node''' 9 | if node.left != None: 10 | node.left.data += diff 11 | increment(node.left, diff) 12 | 13 | elif node.right != None: 14 | node.right.data += diff 15 | increment(node.right, diff) 16 | 17 | # Time-complexity: O(n^2) worst case 18 | def convert_tree_to_children_sum_property_tree(node): 19 | '''Convert tree such that it holds child sum property''' 20 | if node == None or (node.left == None and node.right == None): 21 | return 22 | 23 | else: 24 | convert_tree_to_children_sum_property_tree(node.left) 25 | convert_tree_to_children_sum_property_tree(node.right) 26 | 27 | left_child_data = 0 28 | right_child_data = 0 29 | 30 | if node.left: 31 | left_child_data = node.left.data 32 | 33 | if node.right: 34 | right_child_data = node.right.data 35 | 36 | diff = node.data - left_child_data - right_child_data 37 | if diff < 0: 38 | node.data += abs(diff) 39 | 40 | elif diff > 0: 41 | increment(node, diff) 42 | 43 | if __name__=='__main__': 44 | root = binary_tree.Node(50) 45 | tree = binary_tree.Tree(root) 46 | root = tree.root 47 | root.left = binary_tree.Node(7) 48 | root.right = binary_tree.Node(2) 49 | root.left.left = binary_tree.Node(3) 50 | root.left.right = binary_tree.Node(5) 51 | root.right.left = binary_tree.Node(1) 52 | root.right.right = binary_tree.Node(30) 53 | 54 | tree.inorder(root) 55 | convert_tree_to_children_sum_property_tree(root) 56 | print "After Conversion to hold child sum property" 57 | tree.inorder(root) 58 | -------------------------------------------------------------------------------- /data-structure-questions/binary_tree/convert_to_sumtree.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | # Program to convert tree to sumTree 4 | import binary_tree 5 | 6 | def convert_to_SumTree(root): 7 | '''Convert a tree to its sum tree''' 8 | if root is None: 9 | return 0 10 | 11 | old_value = root.data 12 | root.data = convert_to_SumTree(root.left) + convert_to_SumTree(root.right) 13 | 14 | return root.data + old_value 15 | 16 | if __name__ == "__main__": 17 | root = binary_tree.Node(10) 18 | root.left = binary_tree.Node(-2) 19 | root.right = binary_tree.Node(6) 20 | root.left.left = binary_tree.Node(8) 21 | root.left.right = binary_tree.Node(-4) 22 | root.right.left = binary_tree.Node(7) 23 | root.right.right = binary_tree.Node(5) 24 | tree = binary_tree.Tree(root) 25 | convert_to_SumTree(tree.root) 26 | -------------------------------------------------------------------------------- /data-structure-questions/binary_tree/convert_tree_to_linked_list.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | # Program to convert a binary tree to doubly linked list 4 | 5 | import binary_tree 6 | 7 | def convert_tree_linked_list(root): 8 | 9 | if root is None: 10 | return None 11 | 12 | if root.left is not None: 13 | 14 | left = convert_tree_linked_list(root.left) 15 | 16 | while left.right: 17 | left = left.right 18 | 19 | left.right = root 20 | root.left = left 21 | 22 | if root.right is not None: 23 | 24 | right = convert_tree_linked_list(root.right) 25 | 26 | while right.left: 27 | right = right.left 28 | 29 | right.left = root 30 | root.right = right 31 | 32 | return root 33 | 34 | tree = binary_tree.construct_binary_tree() 35 | 36 | tree.inorder(tree.root) 37 | 38 | root = convert_tree_linked_list(tree.root) 39 | 40 | # Node returned will be root of the previous tree. 41 | # Inorder to print the linked list, we have to iterate to its left 42 | while root.left: 43 | root = root.left 44 | 45 | print "After conversion" 46 | 47 | while (root): 48 | print root.data 49 | root = root.right 50 | -------------------------------------------------------------------------------- /data-structure-questions/binary_tree/count_leaf_nodes.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | ''' 4 | Program to count leaf nodes in a binary tree. 5 | 6 | A node is a leaf node if both left and right child nodes of it are NULL. 7 | 8 | Time Complexity: O(n) 9 | ''' 10 | 11 | import binary_tree 12 | 13 | def count_leaf_nodes(root): 14 | if root is None: 15 | return 0 16 | 17 | if root.left is None and root.right is None: 18 | return 1 19 | 20 | else: 21 | return count_leaf_nodes(root.left) + count_leaf_nodes(root.right) 22 | 23 | if __name__ == "__main__": 24 | tree = binary_tree.construct_binary_tree() 25 | print count_leaf_nodes(tree.root) 26 | -------------------------------------------------------------------------------- /data-structure-questions/binary_tree/deepest_left_leaf_node.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | # Deepest left leaf node in a Binary Tree 4 | 5 | # 1. Take two arguments root and its level 6 | # 2. Global deepest_node declared, and deepest level 7 | # 3. Do postorder traversal and chek if a node is leaf node and if its level is greater then deepest_level then assign it values 8 | 9 | import binary_tree 10 | 11 | # Global variables keep track of deepest leaf node level and its value 12 | deepest_level = -1 13 | deepest_node = None 14 | 15 | def deepest_level_leaf_node(root, level): 16 | global deepest_level 17 | global deepest_node 18 | 19 | if not root: 20 | return 21 | 22 | if not root.left and not root.right and level > deepest_level: 23 | deepest_level = level 24 | deepest_node = root.data 25 | 26 | deepest_level_leaf_node(root.left, level+1) 27 | deepest_level_leaf_node(root.right, level+1) 28 | 29 | 30 | tree = binary_tree.construct_binary_tree() 31 | deepest_level_leaf_node(tree.root, 1) 32 | print deepest_node -------------------------------------------------------------------------------- /data-structure-questions/binary_tree/diameter.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | ''' 4 | The diameter of a tree (sometimes called the width) is the number of nodes on the longest path between two 5 | leaves in the tree. 6 | ''' 7 | 8 | import binary_tree 9 | 10 | def height(root): 11 | '''Calculate and return the height of the tree''' 12 | if not root: 13 | return 0 14 | 15 | else: 16 | return max(height(root.left), height(root.right)) + 1 17 | 18 | # Time Complexity: O(n^2) 19 | def diameter(root): 20 | '''Calculate the diameter of the tree''' 21 | if not root: 22 | return 0 23 | 24 | else: 25 | lh = height(root.left) 26 | rh = height(root.right) 27 | 28 | ld = diameter(root.left) 29 | rd = diameter(root.right) 30 | 31 | return max(lh+rh+1, max(ld, rd)) 32 | 33 | if __name__ == "__main__": 34 | tree = binary_tree.construct_binary_tree() 35 | print diameter(tree.root) -------------------------------------------------------------------------------- /data-structure-questions/binary_tree/double_tree.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | ''' 4 | Write a program that converts a given tree to its Double tree. To create Double tree of the given tree, 5 | create a new duplicate for each node, and insert the duplicate as the left child of the original node. 6 | ''' 7 | import binary_tree 8 | 9 | def double_tree_1(root): 10 | '''Convert the tree to its double tree''' 11 | if root is None: 12 | return 13 | else: 14 | temp = root.left 15 | root.left = binary_tree.Node(root.data) 16 | root.left.left = temp 17 | 18 | double_tree_1(root.left.left) 19 | double_tree_1(root.right) 20 | 21 | return root 22 | 23 | def double_tree(root): 24 | '''Convert the tree to its double tree''' 25 | if root is None: 26 | return 27 | else: 28 | 29 | double_tree(root.left) 30 | double_tree(root.right) 31 | 32 | temp = root.left 33 | root.left = binary_tree.Node(root.data) 34 | root.left.left = temp 35 | 36 | return root 37 | 38 | if __name__ == "__main__": 39 | tree = binary_tree.construct_binary_tree() 40 | 41 | tree.inorder(tree.root) 42 | print "Conversion to double tree using method 1" 43 | tree.root = double_tree_1(tree.root) 44 | tree.inorder(tree.root) 45 | 46 | tree = binary_tree.construct_binary_tree() 47 | print "Conversion to double tree using method 2" 48 | tree.root = double_tree(tree.root) 49 | tree.inorder(tree.root) 50 | -------------------------------------------------------------------------------- /data-structure-questions/binary_tree/duplicate_subtree_in_binary_tree.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Find if a given binary tree has duplicate sub trees or not. 3 | (Two leaf nodes of a parent do not count as subtree) 4 | ''' 5 | 6 | inorder_subtrees = [] 7 | def get_inorder_subtrees(root): 8 | if not root.left and not root.right: 9 | return str(root.data) 10 | temp = '' 11 | 12 | if root.left: 13 | temp += get_inorder_subtrees(root.left) 14 | 15 | temp += str(root.data) 16 | 17 | if root.right: 18 | temp += get_inorder_subtrees(root.right) 19 | 20 | inorder_subtrees.append(temp) 21 | 22 | return temp 23 | 24 | postorder_subtrees = [] 25 | def get_postorder_subtrees(root): 26 | if not root.left and not root.right: 27 | return str(root.data) 28 | temp = '' 29 | 30 | if root.left: 31 | temp += get_postorder_subtrees(root.left) 32 | 33 | if root.right: 34 | temp += get_postorder_subtrees(root.right) 35 | 36 | temp += str(root.data) 37 | 38 | postorder_subtrees.append(temp) 39 | 40 | return temp 41 | 42 | def compare_subtrees(): 43 | global inorder_subtrees 44 | global postorder_subtrees 45 | 46 | hash_map = {} 47 | for inorder, postorder in zip(inorder_subtrees, postorder_subtrees): 48 | key = inorder + '-' + postorder 49 | if key in hash_map: 50 | return True 51 | else: 52 | hash_map[key] = 1 53 | return False 54 | 55 | def duplicate_subtree(tree): 56 | get_inorder_subtrees(tree.root) 57 | get_postorder_subtrees(tree.root) 58 | 59 | answer = compare_subtrees() 60 | 61 | return answer 62 | 63 | if __name__ == "__main__": 64 | import binary_tree 65 | tree = binary_tree.construct_binary_tree() 66 | print duplicate_subtree(tree) 67 | -------------------------------------------------------------------------------- /data-structure-questions/binary_tree/extract_leaves_of_binary_tree.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | # Program to extract the leaves of a binary tree in a doubly linked list 4 | 5 | import binary_tree 6 | 7 | class Node(object): 8 | 9 | def __init__(self, data): 10 | 11 | self.data = data 12 | self.nextnode = None 13 | 14 | head = None 15 | 16 | def extract_leaves(root): 17 | global head 18 | if root == None: 19 | return None 20 | 21 | if not root.left and not root.right: 22 | temp = Node(root.data) 23 | 24 | if head is not None: 25 | temp.nextnode = head 26 | 27 | head = temp 28 | return None 29 | 30 | root.right = extract_leaves(root.right) 31 | root.left = extract_leaves(root.left) 32 | 33 | return root 34 | 35 | tree = binary_tree.construct_binary_tree() 36 | tree.inorder(tree.root) 37 | 38 | root = extract_leaves(tree.root) 39 | print "After extracting leaves" 40 | tree.inorder(root) 41 | 42 | print "Linked list" 43 | while head: 44 | print head.data 45 | head = head.nextnode 46 | -------------------------------------------------------------------------------- /data-structure-questions/binary_tree/find_the_closest_leaf_in_binary_tree.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | # Find the closest leaf in a Binary Tree 4 | 5 | import binary_tree 6 | import sys 7 | 8 | def closest_leaf_util(root): 9 | if root is None: 10 | return sys.maxint 11 | 12 | if not root.left and not root.right: 13 | return 0 14 | 15 | else: 16 | return min(closest_leaf_util(root.left), closest_leaf_util(root.right)) + 1 17 | 18 | def closest_leaf(root, value, ancestors, index): 19 | 20 | if root is None: 21 | return sys.maxint 22 | 23 | if root.data == value: 24 | # check for closest leaf node distance downwards 25 | distance = closest_leaf_util(root) 26 | 27 | # Look for the closest leaf node distance for all its ancestors 28 | for i in range(index-1, -1, -1): 29 | distance = min(distance, index-i+closest_leaf_util(ancestors[i])) 30 | 31 | return distance 32 | 33 | if index >= len(ancestors): 34 | ancestors.append(root) 35 | else: 36 | ancestors[index] = root 37 | 38 | return min(closest_leaf(root.left, value, ancestors, index+1), closest_leaf(root.right, value, ancestors, index+1)) 39 | 40 | tree = binary_tree.construct_binary_tree() 41 | # List that will store ancestors at every index 42 | ancestors = [] 43 | print closest_leaf(tree.root, 'C', ancestors, 0) -------------------------------------------------------------------------------- /data-structure-questions/binary_tree/foldable_binary_tree.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | ''' 4 | Foldable Binary Tree. Program to find if a tree can be folded or not. A tree can be folded if left and 5 | right subtrees of the tree are structure wise mirror image of each other. 6 | ''' 7 | import binary_tree 8 | 9 | def mirror(root1, root2): 10 | '''Check if the two sub trees are mirror or not''' 11 | if not root1 and not root2: 12 | return True 13 | elif not root1 or not root2: 14 | return False 15 | elif mirror(root1.right, root2.left) and mirror(root1.left, root2.right): 16 | return True 17 | else: 18 | return False 19 | 20 | def foldable_tree(root): 21 | '''Check if teh tree can be folded or not''' 22 | if root is None: 23 | return True 24 | 25 | else: 26 | if mirror(root.left, root.right): 27 | return True 28 | else: 29 | return False 30 | 31 | if __name__ == "__main__": 32 | tree = binary_tree.construct_binary_tree() 33 | print foldable_tree(tree.root) 34 | -------------------------------------------------------------------------------- /data-structure-questions/binary_tree/half_tree.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | # Program to find half tree 4 | 5 | import binary_tree 6 | 7 | def half_tree(root): 8 | if root is None: 9 | return None 10 | 11 | root.left = half_tree(root.left) 12 | root.right = half_tree(root.right) 13 | 14 | if not root.left and not root.right: 15 | return root 16 | 17 | if not root.left: 18 | return root.right 19 | 20 | if not root.right: 21 | return root.left 22 | 23 | return root 24 | 25 | def inorder(root): 26 | if root is not None: 27 | inorder(root.left) 28 | print root.data 29 | inorder(root.right) 30 | 31 | tree = binary_tree.construct_binary_tree() 32 | root = half_tree(tree.root) 33 | 34 | inorder(root) 35 | -------------------------------------------------------------------------------- /data-structure-questions/binary_tree/identical_trees.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | # Identical Trees 4 | 5 | ''' 6 | Two trees are identical when they have same data and arrangement of data is also same. 7 | ''' 8 | 9 | 10 | import binary_tree 11 | 12 | def identical_trees(root1, root2): 13 | '''Return true if two trees are identical otherwise returns false''' 14 | if not root1 and not root2: 15 | return True 16 | 17 | if not root1 or not root2: 18 | return False 19 | 20 | else: 21 | if root1.data == root2.data and identical_trees(root1.left, root2.left) and identical_trees(root1.right, root2.right): 22 | return True 23 | else: 24 | return False 25 | 26 | if __name__ == "__main__": 27 | tree1 = binary_tree.construct_binary_tree() 28 | tree2 = binary_tree.construct_binary_tree( 29 | print identical_trees(tree1.root, tree2.root) -------------------------------------------------------------------------------- /data-structure-questions/binary_tree/inorder_successor.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | # Populate inorder successor of tree 4 | 5 | 6 | class Node(object): 7 | def __init__(self, data=None, left=None, right=None, next=None): 8 | self.data = data 9 | self.left = left 10 | self.right = right 11 | self.next = next 12 | 13 | next = None 14 | 15 | def inorder(root): 16 | 17 | while root.left: 18 | root = root.left 19 | 20 | temp = root 21 | 22 | while temp: 23 | print temp.data 24 | temp = temp.next 25 | 26 | def inorder_successor(root): 27 | 28 | global next 29 | 30 | if not root: 31 | return 32 | 33 | inorder_successor(root.right) 34 | 35 | root.next = next 36 | next = root 37 | 38 | inorder_successor(root.left) 39 | 40 | def construct_binary_tree(): 41 | root = Node(26) 42 | root.left = Node(10) 43 | root.right = Node(3) 44 | root.left.left = Node(4) 45 | root.left.right = Node(6) 46 | root.right.right = Node(13) 47 | root.right.left = Node(5) 48 | 49 | return root 50 | 51 | 52 | root = construct_binary_tree() 53 | inorder_successor(root) 54 | inorder(root) -------------------------------------------------------------------------------- /data-structure-questions/binary_tree/inorder_without_recursion.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | import binary_tree 4 | 5 | # Program to print inorder traversal of tree without using recursion and using stack 6 | 7 | def inorder_without_recursion(node): 8 | if node is None: 9 | return 10 | 11 | stack = [] 12 | current = node 13 | done = False 14 | 15 | while(not done): 16 | 17 | if current: 18 | stack.append(current) 19 | current = current.left 20 | 21 | else: 22 | if stack: 23 | last_item = stack.pop(-1) 24 | print last_item.data 25 | 26 | if last_item.right != None: 27 | current = last_item.right 28 | 29 | else: 30 | done = True 31 | 32 | if __name__ == '__main__': 33 | tree = binary_tree.construct_binary_tree() 34 | 35 | print "Inorder traversal without using recursion" 36 | inorder_without_recursion(tree.root) -------------------------------------------------------------------------------- /data-structure-questions/binary_tree/is_height_balanced_tree.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | # Programt o check if a tree is height balanced or not. A tree is height balanced if difference in height of left and right subtree is not greater than 1 4 | 5 | import binary_tree 6 | 7 | def height(root): 8 | '''Calculate the height of the tree''' 9 | if root is None: 10 | return 0 11 | 12 | else: 13 | left_height = height(root.left) 14 | right_height = height(root.right) 15 | 16 | return max(left_height, right_height) + 1 17 | 18 | def is_balanced_tree(node): 19 | '''Return true if tree is height balanced''' 20 | if node == None: 21 | return True 22 | 23 | else: 24 | left_height = height(node.left) 25 | right_height = height(node.right) 26 | 27 | if abs(left_height - right_height) <= 1 and is_balanced_tree(node.left) and is_balanced_tree(node.right): 28 | return True 29 | 30 | return False 31 | 32 | if __name__=="__main__": 33 | tree = binary_tree.construct_binary_tree() 34 | print is_balanced_tree(tree.root) 35 | -------------------------------------------------------------------------------- /data-structure-questions/binary_tree/isomorphic_trees.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | ''' 4 | Write a function to detect if two trees are isomorphic. Two trees are called isomorphic if one of them can be 5 | obtained from other by a series of flips, i.e. by swapping left and right children of a number of nodes. Any 6 | number of nodes at any level can have their children swapped. Two empty trees are isomorphic. 7 | ''' 8 | 9 | def isomorphic_trees(a, b): 10 | if a is None and b is None: 11 | return True 12 | 13 | if a is None or b is None: 14 | return False 15 | 16 | if a.data != b.data: 17 | return False 18 | 19 | return (isomorphic_trees(a.left, b.left) and isomorphic_trees(a.right, b.right)) \ 20 | or (isomorphic_trees(a.right, b.left) and isomorphic_trees(a.left, b.right)) 21 | 22 | if __name__ == "__main__": 23 | import binary_tree 24 | 25 | n1 = binary_tree.Node(1) 26 | n1.left = binary_tree.Node(2) 27 | n1.right = binary_tree.Node(3) 28 | n1.left.left = binary_tree.Node(4) 29 | n1.left.right = binary_tree.Node(5) 30 | n1.right.left = binary_tree.Node(6) 31 | n1.left.right.left = binary_tree.Node(7) 32 | n1.left.right.right = binary_tree.Node(8) 33 | 34 | n2 = binary_tree.Node(1) 35 | n2.left = binary_tree.Node(3) 36 | n2.right = binary_tree.Node(2) 37 | n2.right.left = binary_tree.Node(4) 38 | n2.right.right = binary_tree.Node(5) 39 | n2.left.right = binary_tree.Node(6) 40 | n2.right.right.left = binary_tree.Node(8) 41 | n2.right.right.right = binary_tree.Node(7) 42 | 43 | print isomorphic_trees(n1, n2) 44 | -------------------------------------------------------------------------------- /data-structure-questions/binary_tree/iterative_postorder_traversal_using_two_stacks.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | # Iterative postorder traversal using two stacks 4 | 5 | import binary_tree 6 | 7 | def postorder(root): 8 | if root is None: 9 | return 10 | 11 | stack1 = [root] 12 | stack2 = [] 13 | 14 | while stack1: 15 | s = stack1.pop(-1) 16 | stack2.append(s) 17 | 18 | if s.left: 19 | stack1.append(s.left) 20 | 21 | if s.right: 22 | stack1.append(s.right) 23 | 24 | while(stack2): 25 | s = stack2.pop(-1) 26 | print s.data 27 | 28 | if __name__ == "__main__": 29 | tree = binary_tree.construct_binary_tree() 30 | postorder(tree.root) 31 | -------------------------------------------------------------------------------- /data-structure-questions/binary_tree/iterative_preorder.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | # Iterative preorder traversal 4 | 5 | import binary_tree 6 | 7 | def iterative_preorder(root): 8 | '''Print preorder traversal using stack''' 9 | if root is None: 10 | return 11 | 12 | stack = [root] 13 | curr = root 14 | 15 | while stack: 16 | s = stack.pop(-1) 17 | print s.data 18 | 19 | if s.right: 20 | stack.append(s.right) 21 | 22 | if s.left: 23 | stack.append(s.left) 24 | 25 | if __name__ == "__main__": 26 | tree = binary_tree.construct_binary_tree() 27 | iterative_preorder(tree.root) 28 | -------------------------------------------------------------------------------- /data-structure-questions/binary_tree/iteratively_search_element_in_binary_tree.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | # Iterative search for a key in a binary tree 4 | 5 | import binary_tree 6 | 7 | def search(root, x): 8 | 9 | if root is None: 10 | return 11 | 12 | queue = [root] 13 | 14 | while queue: 15 | 16 | node = queue[0] 17 | 18 | if node.data == x: 19 | return True 20 | 21 | del queue[0] 22 | 23 | if node.left != None: 24 | queue.append(node.left) 25 | if node.right != None: 26 | queue.append(node.right) 27 | 28 | return False 29 | 30 | tree = binary_tree.construct_binary_tree() 31 | 32 | print search(tree.root, 100) 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /data-structure-questions/binary_tree/left_leaves_sum.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | # Program to find sum of all left leaves 4 | 5 | import binary_tree 6 | 7 | max_sum = 0 8 | 9 | def sum_left_leaves(root): 10 | global max_sum 11 | 12 | if root is None: 13 | return 14 | 15 | if root.left and not root.left.left and not root.left.right: 16 | max_sum += root.left.data 17 | 18 | sum_left_leaves(root.left) 19 | sum_left_leaves(root.right) 20 | 21 | 22 | tree = binary_tree.construct_binary_tree() 23 | sum_left_leaves(tree.root) 24 | print max_sum 25 | -------------------------------------------------------------------------------- /data-structure-questions/binary_tree/left_view_of_tree.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | # Program to print left view of a tree 4 | 5 | import binary_tree 6 | 7 | max_level = 0 8 | 9 | def left_view_of_tree(node, level): 10 | global max_level 11 | 12 | if node is None: 13 | return 14 | 15 | if max_level < level: 16 | print node.data, level, max_level 17 | max_level = level 18 | 19 | left_view_of_tree(node.left, level+1) 20 | left_view_of_tree(node.right, level+1) 21 | 22 | 23 | if __name__=="__main__": 24 | tree = binary_tree.construct_binary_tree() 25 | print "Printing lef view of tree" 26 | left_view_of_tree(tree.root, 1) -------------------------------------------------------------------------------- /data-structure-questions/binary_tree/level_of_node.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | # Program to find level of a node in a binary tree 4 | 5 | import binary_tree 6 | import copy 7 | 8 | def level_of_node(root, node, level): 9 | if root is None: 10 | return 0 11 | 12 | if root.data == node: 13 | return level 14 | 15 | else: 16 | return max(level_of_node(root.left, node, level+1), level_of_node(root.right, node, level+1)) 17 | 18 | 19 | def find_level(): 20 | tree = binary_tree.construct_binary_tree() 21 | level = 0 22 | print level_of_node(tree.root, 8, level+1) 23 | 24 | 25 | if __name__=="__main__": 26 | find_level() -------------------------------------------------------------------------------- /data-structure-questions/binary_tree/level_order_traversal.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | # Level order tree traversal 4 | 5 | ''' 6 | Level order traversal of a tree is also called breadth first traversal of the tree. 7 | 8 | METHOD 1: (Use function to print a given level) 9 | 10 | Time complexity: O(n*n) 11 | ''' 12 | 13 | import binary_tree 14 | 15 | def level_order_traversal_helper(root, level): 16 | '''Print the node if the level is 1''' 17 | if root is None: 18 | return 19 | 20 | if level == 1: 21 | print root.data 22 | 23 | else: 24 | level_order_traversal_helper(root.left, level-1) 25 | level_order_traversal_helper(root.right, level-1) 26 | 27 | def level_order_traversal(tree): 28 | '''Call the level order helper at each level of the tree''' 29 | height = tree.height(tree.root) 30 | 31 | for i in range(1, height+1): 32 | level_order_traversal_helper(tree.root, i) 33 | 34 | ''' 35 | Method 2: Using queue 36 | 37 | Time Complexity: O(n) 38 | ''' 39 | 40 | import Queue 41 | 42 | def level_order_traversal_using_queue(root): 43 | '''Print level order traversal using queue''' 44 | q = Queue.Queue() 45 | 46 | q.put(root) 47 | while not q.empty(): 48 | temp = q.get() 49 | print temp.data 50 | 51 | if temp.left: 52 | q.put(temp.left) 53 | 54 | if temp.right: 55 | q.put(temp.right) 56 | 57 | if __name__ == "__main__": 58 | # Construct binary tree 59 | tree = binary_tree.construct_binary_tree() 60 | print "Level order traversal" 61 | level_order_traversal(tree) 62 | print "Level order traversal using queue" 63 | level_order_traversal_using_queue(tree.root) 64 | -------------------------------------------------------------------------------- /data-structure-questions/binary_tree/level_order_traversal_line_by_line.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | # Print level order traversal line by line using iterative level order traversal 4 | 5 | from __future__ import print_function 6 | import binary_tree 7 | import Queue 8 | 9 | 10 | def level_order_traversal_helper(root, level): 11 | '''Print the node if the level is 1''' 12 | if root is None: 13 | return 14 | 15 | if level == 1: 16 | print(root.data, end=" ") 17 | 18 | else: 19 | level_order_traversal_helper(root.left, level-1) 20 | level_order_traversal_helper(root.right, level-1) 21 | 22 | def level_order_traversal(tree): 23 | '''Call the level order helper at each level of the tree''' 24 | height = tree.height(tree.root) 25 | 26 | for i in range(1, height+1): 27 | level_order_traversal_helper(tree.root, i) 28 | print ("\n") 29 | 30 | def level_order_traversal_using_queue(root): 31 | '''Print level order traversal using queue''' 32 | q = Queue.Queue() 33 | 34 | q.put(root) 35 | while not q.empty(): 36 | number_of_nodes = q.qsize() 37 | 38 | while (number_of_nodes): 39 | temp = q.get() 40 | print(temp.data, end=" ") 41 | 42 | if temp.left: 43 | q.put(temp.left) 44 | 45 | if temp.right: 46 | q.put(temp.right) 47 | 48 | number_of_nodes -= 1 49 | 50 | print("\n") 51 | 52 | if __name__ == "__main__": 53 | tree = binary_tree.construct_binary_tree() 54 | level_order_traversal(tree) 55 | print("Level order traversal using queue") 56 | level_order_traversal_using_queue(tree.root) 57 | -------------------------------------------------------------------------------- /data-structure-questions/binary_tree/lowest_common_ancestor_binary_tree.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | # Program to find lowest common ancestor of binary tree 4 | 5 | import binary_tree 6 | 7 | def find_path(root, path, node1): 8 | 9 | if root is None: 10 | return False 11 | 12 | path.append(root.data) 13 | 14 | if root.data == node1: 15 | return path 16 | 17 | if (root.left and find_path(root.left, path, node1)) or (root.right and find_path(root.right, path, node1)): 18 | return True 19 | 20 | del path[-1] 21 | 22 | return False 23 | 24 | def lowest_common_ancestor(root, node1, node2): 25 | 26 | path1 = [] 27 | path2 = [] 28 | 29 | if not find_path(root, path1, node1) or not find_path(root, path2, node2): 30 | return -1 31 | 32 | i = 0 33 | while (i < len(path1) and i < len(path2)): 34 | if path1[i] != path2[i]: 35 | break 36 | i += 1 37 | 38 | print path1[i-1] 39 | 40 | if __name__=="__main__": 41 | tree = binary_tree.construct_binary_tree() 42 | 43 | lowest_common_ancestor(tree.root, 5, 4) 44 | -------------------------------------------------------------------------------- /data-structure-questions/binary_tree/max_width.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | # Program to print the maximum width of a tree 4 | 5 | import binary_tree 6 | 7 | def max_width(tree): 8 | '''Return the maximum width of the binary tree''' 9 | h = tree.height(tree.root) 10 | 11 | # count stores the number of nodes at each level 12 | count = [0] * h 13 | max_width_util(tree.root, count, 0) 14 | 15 | return max(count) 16 | 17 | def max_width_util(root, count, level): 18 | '''Increase the count at each level''' 19 | if root: 20 | count[level] += 1 21 | max_width_util(root.left, count, level+1) 22 | max_width_util(root.right, count, level+1) 23 | 24 | if __name__=="__main__": 25 | tree = binary_tree.construct_binary_tree() 26 | print max_width(tree) 27 | -------------------------------------------------------------------------------- /data-structure-questions/binary_tree/max_width_non_recursive.py: -------------------------------------------------------------------------------- 1 | 2 | max_width = 0 3 | from Queue import * 4 | import binary_tree 5 | 6 | def maximum_width(root): 7 | global max_width 8 | if not root: 9 | return 0 10 | 11 | else: 12 | queue = Queue() 13 | queue.put(root) 14 | 15 | while 1: 16 | numiterations = queue.qsize() 17 | if numiterations > max_width: 18 | max_width = numiterations 19 | 20 | if numiterations == 0: 21 | break 22 | 23 | while numiterations > 0: 24 | s = queue.get() 25 | 26 | if s.left: 27 | queue.put(s.left) 28 | if s.right: 29 | queue.put(s.right) 30 | numiterations -= 1 31 | 32 | return max_width 33 | 34 | tree = binary_tree.construct_binary_tree() 35 | print maximum_width(tree.root) -------------------------------------------------------------------------------- /data-structure-questions/binary_tree/maximum_sum_root_to_leaf_path.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | # Program to find maximum sum of root to leaf 4 | 5 | import binary_tree 6 | 7 | max_sum = 0 8 | 9 | def maximum_root_to_leaf(root, path, index): 10 | '''Calculate the maximu root to leaf path''' 11 | global max_sum 12 | 13 | if not root: 14 | return 15 | 16 | else: 17 | if index >= len(path): 18 | path.append(root.data) 19 | else: 20 | path[index] = root.data 21 | 22 | # If we reach leaf nodes, then calculate the path suma and compare with max_sum 23 | if not root.left and not root.right: 24 | if sum(path[0:index+1]) > max_sum: 25 | max_sum = sum(path[0:index+1]) 26 | 27 | maximum_root_to_leaf(root.left, path, index+1) 28 | maximum_root_to_leaf(root.right, path, index+1) 29 | 30 | if __name__ == "__main__": 31 | tree = binary_tree.construct_binary_tree() 32 | 33 | maximum_root_to_leaf(tree.root, [], 0) 34 | print max_sum 35 | -------------------------------------------------------------------------------- /data-structure-questions/binary_tree/mirror_binary_tree.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | # Program to find mirror of a binary tree 4 | 5 | import binary_tree 6 | 7 | def mirror(root): 8 | '''Convert the tree to its mirror tree''' 9 | if root is None or (root.left is None and root.right is None): 10 | return 11 | 12 | else: 13 | mirror(root.left) 14 | mirror(root.right) 15 | 16 | temp = root.left 17 | root.left = root.right 18 | root.right = temp 19 | 20 | if __name__=="__main__": 21 | tree = binary_tree.construct_binary_tree() 22 | tree.inorder(tree.root) 23 | # Change the tree to its mirror tree 24 | mirror(tree.root) 25 | print "After changing the tree to its mirror" 26 | tree.inorder(tree.root) 27 | -------------------------------------------------------------------------------- /data-structure-questions/binary_tree/morris_traversal_inorder.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | # Morris Traversal: Inorder binary tree traversal without recursion and stack 4 | 5 | import binary_tree 6 | 7 | def find_inorder_pred(current): 8 | '''Find inorder predecessor of a node''' 9 | pred = current.left 10 | if pred.right is not None and pred.right != current: 11 | pred = pred.right 12 | 13 | return pred 14 | 15 | def inorder(root): 16 | '''Print inorder traversal of a binary tree''' 17 | if root is None: 18 | return 19 | 20 | current = root 21 | 22 | while current is not None: 23 | if current.left is None: 24 | print current.data 25 | current = current.right 26 | 27 | elif current.left is not None: 28 | pred = find_inorder_pred(current) 29 | 30 | if pred.right is None: 31 | pred.right = current 32 | current = current.left 33 | 34 | else: 35 | pred.right = None 36 | print current.data 37 | current = current.right 38 | 39 | if __name__ == "__main__": 40 | # construct binary tree 41 | tree = binary_tree.construct_binary_tree() 42 | 43 | inorder(tree.root) 44 | -------------------------------------------------------------------------------- /data-structure-questions/binary_tree/morris_traversal_preorder.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | # Morris Traversal: Inorder binary tree traversal without recursion and stack 4 | 5 | def preorder(root): 6 | if not root: 7 | return 8 | 9 | else: 10 | current = root 11 | 12 | while (current is not None): 13 | if current.left is None: 14 | print current.data 15 | current = current.right 16 | 17 | else: 18 | if current.left is not None: 19 | pred = current.left 20 | while (pred.right is not None and pred.right != current): 21 | pred = pred.right 22 | 23 | if pred.right is None: 24 | print current.data 25 | pred.right = current 26 | current = current.left 27 | 28 | else: 29 | pred.right = None 30 | current = current.right 31 | 32 | if __name__ == "__main__": 33 | import binary_tree 34 | # construct binary tree 35 | tree = binary_tree.construct_binary_tree() 36 | preorder(tree.root) 37 | -------------------------------------------------------------------------------- /data-structure-questions/binary_tree/print_all_ancestors_of_node.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | # Program to find all ancestors of a node in a binary tree 4 | 5 | import binary_tree 6 | 7 | def find_ancestors(root, node, index, path): 8 | '''Print all the ancestors of a node''' 9 | if root is None: 10 | return 11 | 12 | if root.data == node: 13 | print path[0:index] 14 | return 15 | 16 | else: 17 | if index < len(path): 18 | path[index] = root.data 19 | else: 20 | path.append(root.data) 21 | 22 | find_ancestors(root.left, node, index+1, path) 23 | find_ancestors(root.right, node, index+1, path) 24 | 25 | if __name__ == "__main__": 26 | tree = binary_tree.construct_binary_tree() 27 | find_ancestors(tree.root, 4, 0, []) 28 | -------------------------------------------------------------------------------- /data-structure-questions/binary_tree/print_all_nodes_at_k_distance.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | # Print all the nodes that are at the kth distance from the root node 4 | 5 | import binary_tree 6 | 7 | def all_nodes_at_k_distance(node, k): 8 | '''Print nodes at k distance from root node''' 9 | if node is None: 10 | return 11 | 12 | if k == 0: 13 | print node.data 14 | 15 | all_nodes_at_k_distance(node.left, k-1) 16 | all_nodes_at_k_distance(node.right, k-1) 17 | 18 | 19 | def k_distance(root, distance, k): 20 | '''Print nodes at k distance from root node''' 21 | if root is None: 22 | return 23 | 24 | if distance == k: 25 | print(root.data) 26 | 27 | else: 28 | k_distance(root.left, distance+1, k) 29 | k_distance(root.right, distance+1, k) 30 | 31 | if __name__=="__main__": 32 | tree = binary_tree.construct_binary_tree() 33 | 34 | print "Nodes at kth distance" 35 | all_nodes_at_k_distance(tree.root, 1) 36 | k_distance(tree.node, 0, 1) 37 | -------------------------------------------------------------------------------- /data-structure-questions/binary_tree/print_tree_vertical_order.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | '''Print a Binary Tree in Vertical Order''' 4 | 5 | import binary_tree 6 | 7 | def vertical_order_print(root, distance, hash_map): 8 | if root is None: 9 | return 10 | 11 | hash_map.setdefault(distance, []) 12 | hash_map[distance].append(root.data) 13 | 14 | vertical_order_print(root.left, distance-1, hash_map) 15 | vertical_order_print(root.right, distance+1, hash_map) 16 | 17 | if __name__ == "__main__": 18 | tree = binary_tree.construct_binary_tree() 19 | 20 | hash_map = {} 21 | vertical_order_print(tree.root, 0, hash_map) 22 | 23 | print hash_map 24 | -------------------------------------------------------------------------------- /data-structure-questions/binary_tree/reverse_alternate_levels.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | # Reverse alternate levels of a perfect binary tree 4 | 5 | import binary_tree 6 | 7 | # Store the nodes at alternate levels by inorder traversal 8 | def store_nodes(root, level, stored_nodes): 9 | if root is None: 10 | return 11 | 12 | store_nodes(root.left, level+1, stored_nodes) 13 | 14 | if level % 2 == 1: 15 | stored_nodes.append(root.data) 16 | 17 | store_nodes(root.right, level+1, stored_nodes) 18 | 19 | # Change the nodes at alternate levels by copying the values from stored_nodes 20 | def reverse(root, level, stored_nodes): 21 | 22 | if not root: 23 | return 24 | 25 | reverse(root.left, level+1, stored_nodes) 26 | 27 | if level % 2 == 1: 28 | root.data = stored_nodes[0] 29 | del stored_nodes[0] 30 | 31 | reverse(root.right, level+1, stored_nodes) 32 | 33 | 34 | tree = binary_tree.construct_binary_tree() 35 | tree.inorder(tree.root) 36 | 37 | stored_nodes = [] 38 | store_nodes(tree.root, 0, stored_nodes) 39 | # Reverse the stored nodes 40 | stored_nodes = stored_nodes[::-1] 41 | 42 | print "Reversing list" 43 | reverse(tree.root, 0, stored_nodes) 44 | tree.inorder(tree.root) 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /data-structure-questions/binary_tree/reverse_level_order_traversal.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | # Print reverse level order traversal. The idea is to print last level first, then second last level, and so on. 4 | 5 | from Queue import * 6 | import binary_tree 7 | 8 | def reverse_level_order(root): 9 | '''Print reverse level order traversal of a tree''' 10 | if not root: 11 | return 12 | 13 | queue = Queue() 14 | queue.put(root) 15 | stack = [] 16 | 17 | while not queue.empty(): 18 | s = queue.get() 19 | stack.append(s) 20 | 21 | if s.left: 22 | queue.put(s.left) 23 | 24 | if s.right: 25 | queue.put(s.right) 26 | 27 | while stack: 28 | s = stack.pop() 29 | print s.data 30 | 31 | if __name__ == "__main__": 32 | tree = binary_tree.construct_binary_tree() 33 | reverse_level_order(tree.root) 34 | -------------------------------------------------------------------------------- /data-structure-questions/binary_tree/root_to_leaf_path.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | # Program to find all the root to leaf paths 4 | 5 | import binary_tree 6 | 7 | def root_to_leaf(root, path, index): 8 | '''Print all the root to leaf paths''' 9 | 10 | if not root: 11 | return 12 | 13 | else: 14 | if index >= len(path): 15 | path.append(root.data) 16 | else: 17 | path[index] = root.data 18 | 19 | # If we reach leaf nodes, then print the path 20 | if not root.left and not root.right: 21 | print path[0:index+1] 22 | 23 | root_to_leaf(root.left, path, index+1) 24 | root_to_leaf(root.right, path, index+1) 25 | 26 | if __name__ == "__main__": 27 | tree = binary_tree.construct_binary_tree() 28 | 29 | root_to_leaf(tree.root, [], 0) 30 | -------------------------------------------------------------------------------- /data-structure-questions/binary_tree/root_to_leaf_pathsum_equal_to_a_given_number.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | ''' 4 | Given a binary tree and a number, return true if the tree has a root-to-leaf path such that adding up 5 | all the values along the path equals the given number. Return false if no such path can be found. 6 | ''' 7 | 8 | # Time complexity: O(n) 9 | def node_to_leaf_sum(root, remaining_sum): 10 | '''Return true if there exists a path with the sum between root to leaf node''' 11 | if root is None: 12 | return remaining_sum == 0 13 | 14 | else: 15 | 16 | sub_sum = remaining_sum - root.data 17 | 18 | if sub_sum == 0 and root.left is None and root.right is None: 19 | return True 20 | 21 | elif sub_sum <= 0: 22 | return False 23 | 24 | else: 25 | return node_to_leaf_sum(root.left, sub_sum) or node_to_leaf_sum(root.right, sub_sum) 26 | 27 | if __name__ == "__main__": 28 | import binary_tree 29 | tree = binary_tree.construct_binary_tree() 30 | 31 | print node_to_leaf_sum(tree.root, 6) 32 | -------------------------------------------------------------------------------- /data-structure-questions/binary_tree/spiral_order_traversal.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | # Print spiral order traversal using iterative method using two stacks 4 | 5 | import binary_tree 6 | 7 | def spiral_order_traversal(root): 8 | '''Print the level order traversal in spiral way''' 9 | if not root: 10 | return 11 | 12 | # It can be done uisng two stacks 13 | s1 = [root] 14 | s2 = [] 15 | 16 | while s1 or s2: 17 | 18 | while s1: 19 | s = s1.pop(-1) 20 | print s.data 21 | 22 | if s.right: 23 | s2.append(s.right) 24 | 25 | if s.left: 26 | s2.append(s.left) 27 | 28 | while s2: 29 | s = s2.pop(-1) 30 | print s.data 31 | 32 | if s.left: 33 | s1.append(s.left) 34 | 35 | if s.right: 36 | s1.append(s.right) 37 | 38 | if __name__ == "__main__": 39 | tree = binary_tree.construct_binary_tree() 40 | spiral_order_traversal(tree.root) 41 | -------------------------------------------------------------------------------- /data-structure-questions/binary_tree/vertical_sum.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | import binary_tree 4 | 5 | # Program to find vertical sum of a tree. 6 | ''' 7 | hd for root is 0, a right edge (edge connecting to right subtree) is considered as +1 horizontal distance 8 | and a left edge is considered as -1 horizontal distance. 9 | ''' 10 | 11 | def vertical_sum(root, distance, hash_map): 12 | '''Calculate the vertical sum of a tree''' 13 | if root is None: 14 | return 15 | 16 | hash_map.setdefault(distance, 0) 17 | hash_map[distance] += root.data 18 | 19 | vertical_sum(root.left, distance-1, hash_map) 20 | vertical_sum(root.right, distance+1, hash_map) 21 | 22 | if __name__ == "__main__": 23 | tree = binary_tree.construct_binary_tree() 24 | 25 | sum_dict = {} 26 | vertical_sum(tree.root, 0, sum_dict) 27 | print sum_dict 28 | -------------------------------------------------------------------------------- /data-structure-questions/graph/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/charulagrl/data-structures-and-algorithms/79ccc0aa34d9e3eb508bfe64d1fcee9dd1ea5e98/data-structure-questions/graph/__init__.py -------------------------------------------------------------------------------- /data-structure-questions/graph/algorithms/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/charulagrl/data-structures-and-algorithms/79ccc0aa34d9e3eb508bfe64d1fcee9dd1ea5e98/data-structure-questions/graph/algorithms/__init__.py -------------------------------------------------------------------------------- /data-structure-questions/graph/algorithms/bellman_ford.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | ''' 4 | Problem: Find the shortest distance to all the nodes from source. 5 | 6 | ''' 7 | 8 | import sys 9 | import adjacency_list_graph 10 | 11 | def bellman_ford(g, source): 12 | 13 | distances = {} 14 | 15 | ''' Initialize a distance dict with all nodes as infinite expect source node ''' 16 | for v in g.adjacency_dict: 17 | if v != source: 18 | distances[v] = sys.maxint 19 | else: 20 | ''' Initialize the source with 0 ''' 21 | distances[source] = 0 22 | 23 | 24 | # It will iterate through len(vertices)-1 times 25 | for _ in range(len(g.adjacency_dict)-1): 26 | # For each iteration check thorugh each vertex in a graph 27 | for u in g.adjacency_dict: 28 | for edge in g.adjacency_dict[u]: 29 | # IF the distance to a node is less than its current distance then update it 30 | if distances[u] != sys.maxint and distances[edge.dest] > distances[u] + edge.weight: 31 | distances[edge.dest] = distances[u] + edge.weight 32 | 33 | return distances 34 | 35 | if __name__ == "__main__": 36 | graph = adjacency_list_graph.AdjacencyListGraph() 37 | 38 | # Initialize the vertex in the graph 39 | s = graph.create_vertex("s") 40 | a = graph.create_vertex("a") 41 | b = graph.create_vertex("b") 42 | c = graph.create_vertex("c") 43 | d = graph.create_vertex("d") 44 | e = graph.create_vertex("e") 45 | 46 | graph.add_edge(s, a, weight=10) 47 | graph.add_edge(s, e, weight=8) 48 | 49 | graph.add_edge(a, c, weight=2) 50 | 51 | graph.add_edge(b, a, weight=1) 52 | 53 | graph.add_edge(c, b, weight=-2) 54 | 55 | graph.add_edge(d, c, weight=-1) 56 | graph.add_edge(d, a, weight=-4) 57 | 58 | graph.add_edge(e, d, weight=1) 59 | 60 | distances = bellman_ford(graph, s) 61 | 62 | for vertex, distance in distances.iteritems(): 63 | print vertex, ": ", distance 64 | -------------------------------------------------------------------------------- /data-structure-questions/graph/algorithms/breadth_first_search.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | # Program to do a breadth first traversal of a graph 4 | 5 | import Queue 6 | 7 | # Time-complexity: O(V+E) 8 | class Graph(object): 9 | def __init__(self, V): 10 | self.V = V 11 | self.adj_list = [[] for i in range(V)] 12 | 13 | def add_edge(self, src, dest): 14 | self.adj_list[src].append(dest) 15 | 16 | def bfs(self, start): 17 | '''Print the breadth first search traversal of the graph''' 18 | # Initialize a visited array with all node as not visited 19 | visited = [0] * graph.V 20 | # Initialize a queue which keeps the visited nodes 21 | queue = Queue.Queue() 22 | # Visit the first node 23 | queue.put(start) 24 | visited[start] = 1 25 | print start 26 | 27 | # Iteratively visit all the nodes in a graph in breadth first search manner 28 | while not queue.empty(): 29 | node = queue.get() 30 | for vertex in self.adj_list[node]: 31 | if not visited[vertex]: 32 | print vertex 33 | visited[vertex] = 1 34 | queue.put(vertex) 35 | 36 | def construct_graph(): 37 | graph = Graph(4) 38 | graph.add_edge(0, 1) 39 | graph.add_edge(0, 2) 40 | graph.add_edge(1, 2) 41 | graph.add_edge(2, 0) 42 | graph.add_edge(2, 3) 43 | graph.add_edge(3, 3) 44 | 45 | return graph 46 | 47 | if __name__ == "__main__": 48 | graph = construct_graph() 49 | graph.bfs(0) 50 | -------------------------------------------------------------------------------- /data-structure-questions/graph/algorithms/depth_first_traversal.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | ''' 4 | Program to do a depth first traversal of a graph. This will print only nodes which are reachable 5 | from the start node. To make sure it traverse all the nodes, you need to create a DFSUtil() for every vertex. 6 | Also, before calling DFSUtil(), we should check if it is already printed by some other call of DFSUtil(). 7 | ''' 8 | 9 | # Time-complexity: O(V+E) 10 | class Graph(object): 11 | def __init__(self, V): 12 | self.V = V 13 | self.adj_list = [[] for i in range(V)] 14 | 15 | def add_edge(self, src, dest): 16 | self.adj_list[src].append(dest) 17 | 18 | def dfs_util(self, start, visited): 19 | '''Print depth first traversal of a graph''' 20 | visited[start] = 1 21 | print start 22 | 23 | for vertex in self.adj_list[start]: 24 | if not visited[vertex]: 25 | self.dfs_util(vertex, visited) 26 | 27 | def dfs(self, start): 28 | visited = [0] * graph.V 29 | self.dfs_util(start, visited) 30 | 31 | def construct_graph(): 32 | graph = Graph(4) 33 | graph.add_edge(0, 1) 34 | graph.add_edge(0, 2) 35 | graph.add_edge(1, 2) 36 | graph.add_edge(2, 0) 37 | graph.add_edge(2, 3) 38 | graph.add_edge(3, 3) 39 | 40 | return graph 41 | 42 | if __name__ == "__main__": 43 | graph = construct_graph() 44 | graph.dfs(2) 45 | -------------------------------------------------------------------------------- /data-structure-questions/graph/algorithms/union_find_algorithm.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | ''' 4 | Problem: Detect cycle in an undirected graph 5 | ''' 6 | 7 | from data_structures import graph 8 | 9 | class UnionFind(object): 10 | def __init__(self, graph): 11 | self.graph = graph 12 | self.parent = self.initialize_parent() 13 | 14 | def get_parent(self, x): 15 | ''' Rreturn the parent of a node until it is 1 ''' 16 | if (self.parent[x] == -1): 17 | return x 18 | 19 | return self.get_parent(self.parent[x]) 20 | 21 | def detect_cycle(self): 22 | ''' Detects if there is a cycle in a graph ''' 23 | edges = self.graph.edges 24 | 25 | for edge in edges: 26 | src = edge.src.name 27 | dest = edge.dest.name 28 | 29 | if (self.get_parent(src) == self.get_parent(dest)): 30 | return True 31 | else: 32 | self.parent[src] = dest 33 | 34 | return False 35 | 36 | def initialize_parent(self): 37 | ''' Initialize a parent dict where each node stores its parent node. 38 | Initially each node is a different subsets with no parent node, hence initialized with -1 ''' 39 | parent = {} 40 | for vertex in self.graph.vertices: 41 | parent[vertex.name] = -1 42 | 43 | return parent 44 | 45 | def test_union_find_algorithm(): 46 | g = graph.construct_graph() 47 | union_find = UnionFind(g) 48 | 49 | assert (union_find.detect_cycle() == True) 50 | 51 | if __name__ == "__main__": 52 | test_union_find_algorithm() 53 | -------------------------------------------------------------------------------- /data-structure-questions/graph/check_bipartitie.py: -------------------------------------------------------------------------------- 1 | # program to check if a graph is a bipartite or not 2 | 3 | class Edge(object): 4 | def __init__(self, src=None, dest=None): 5 | self.src=src 6 | self.dest=dest 7 | 8 | class Graph(object): 9 | def __init__(self, V=None, edges=[]): 10 | self.V = V 11 | self.edges = edges 12 | 13 | 14 | def check_bipartite(graph, src): 15 | 16 | color = [-1] * graph.V 17 | 18 | color[src] = 0 19 | queue = [] 20 | queue.append(src) 21 | 22 | while(queue): 23 | v = queue[0] 24 | del queue[0] 25 | 26 | connected_nodes = graph.edges[v] 27 | 28 | for node in connected_nodes: 29 | if color[node] == -1: 30 | color[node] = 1 - color[v] 31 | 32 | elif color[node] == color[v]: 33 | return False 34 | 35 | return True 36 | 37 | 38 | if __name__=='__main__': 39 | graph = Graph(4, [None]*4) 40 | 41 | graph.edges = [[] for i in graph.edges] 42 | 43 | edge = Edge(0, 1) 44 | graph.edges[edge.src].append(edge.dest) 45 | 46 | edge = Edge(0, 3) 47 | graph.edges[edge.src].append(edge.dest) 48 | 49 | edge = Edge(1, 0) 50 | graph.edges[edge.src].append(edge.dest) 51 | 52 | edge = Edge(1, 2) 53 | graph.edges[edge.src].append(edge.dest) 54 | 55 | edge = Edge(2, 1) 56 | graph.edges[edge.src].append(edge.dest) 57 | 58 | edge = Edge(2, 3) 59 | graph.edges[edge.src].append(edge.dest) 60 | 61 | edge = Edge(3, 0) 62 | graph.edges[edge.src].append(edge.dest) 63 | 64 | edge = Edge(3, 2) 65 | graph.edges[edge.src].append(edge.dest) 66 | 67 | print graph.edges 68 | 69 | print check_bipartite(graph, 2) 70 | -------------------------------------------------------------------------------- /data-structure-questions/graph/cycle_detection_in_directed_graph.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | # Program to detect cycle in an directed graph 4 | 5 | # Time-complexity: O(V+E) 6 | class Graph(object): 7 | def __init__(self, V): 8 | self.V = V 9 | self.adj_list = [[] for i in range(V)] 10 | 11 | def addEdge(self, src, dest): 12 | self.adj_list[src].append(dest) 13 | 14 | def is_cyclic(self): 15 | visited = [0] * self.V 16 | rec_stack = [0] * self.V 17 | 18 | for i in range(self.V): 19 | if self.is_cycle_util(i, visited, rec_stack): 20 | return True 21 | 22 | return False 23 | 24 | def is_cycle_util(self, v, visited, rec_stack): 25 | if not visited[v]: 26 | visited[v] = True 27 | rec_stack[v] = True 28 | 29 | for vertex in self.adj_list[v]: 30 | # If visiting this vertex further leads to a cycle 31 | if not visited[vertex] and self.is_cycle_util(vertex, visited, rec_stack): 32 | return True 33 | 34 | # If the vertex is found in the rec_stack 35 | elif rec_stack[vertex]: 36 | return True 37 | 38 | # remove the vertex from recursion stack 39 | rec_stack[v] = False 40 | return False 41 | 42 | def construct_graph(): 43 | graph = Graph(4) 44 | graph.addEdge(0, 1) 45 | graph.addEdge(0, 2) 46 | graph.addEdge(1, 2) 47 | graph.addEdge(2, 0) 48 | graph.addEdge(2, 3) 49 | graph.addEdge(3, 3) 50 | 51 | return graph 52 | 53 | if __name__ == "__main__": 54 | graph = construct_graph() 55 | print graph.is_cyclic() 56 | -------------------------------------------------------------------------------- /data-structure-questions/graph/detect_cycle_direct_graph_using_colors.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | ''' 3 | Detect cycle in a graph using three colors notation for each vertex and depth first traversal of the tree. 4 | 5 | WHITE(0) : Vertex is not processed yet. Initially all vertices are WHITE. 6 | 7 | GRAY(1) : Vertex is being processed (DFS for this vertex has started, but not finished which means that 8 | all descendants (ind DFS tree) of this vertex are not processed yet (or this vertex is in function 9 | call stack) 10 | 11 | BLACK(2) : Vertex and all its descendants are processed. 12 | 13 | While doing DFS, if we encounter an edge from current vertex to a GRAY vertex, then this edge is back edge 14 | and hence there is a cycle. 15 | ''' 16 | 17 | # Time complexity: O(V+E) 18 | class Graph(object): 19 | def __init__(self, V): 20 | self.V = V 21 | self.adj_list = [[] for i in range(V)] 22 | 23 | def addEdge(self, src, dest): 24 | self.adj_list[src].append(dest) 25 | 26 | 27 | def detect_cycle_util(self, start, colors): 28 | if colors[start] == 0: 29 | colors[start] = 1 30 | for vertex in self.adj_list[start]: 31 | if self.detect_cycle_util(vertex, colors): 32 | return True 33 | 34 | colors[start] = 2 35 | 36 | elif colors[start] == 1: 37 | return True 38 | 39 | else: 40 | return False 41 | 42 | def detect_cycle(self): 43 | colors = [0] * graph.V 44 | 45 | for v in range(graph.V): 46 | if self.detect_cycle_util(v, colors): 47 | return True 48 | 49 | return False 50 | 51 | def construct_graph(): 52 | graph = Graph(4) 53 | graph.addEdge(0, 1) 54 | graph.addEdge(0, 2) 55 | graph.addEdge(1, 2) 56 | graph.addEdge(2, 0) 57 | graph.addEdge(2, 3) 58 | graph.addEdge(3, 3) 59 | 60 | return graph 61 | 62 | if __name__ == "__main__": 63 | graph = construct_graph() 64 | print graph.detect_cycle() 65 | -------------------------------------------------------------------------------- /data-structure-questions/graph/detect_cycle_in_undirected_graph.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | ''' 4 | Program to detect cycle in an undirected graph using DFS traversal. We do a DFS 5 | traversal of the given graph. For every visited vertex ‘v’, if there is an adjacent 6 | ‘u’ such that u is already visited and u is not parent of v, then there is a cycle in graph. 7 | If we don’t find such an adjacent for any vertex, we say that there is no cycle. The assumption 8 | of this approach is that there are no parallel edges between any two vertices. 9 | ''' 10 | 11 | # Time-complexity: O(V+E) 12 | class Graph(object): 13 | 14 | def __init__(self, V): 15 | self.V = V 16 | self.adj_list = [[] for i in range(self.V)] 17 | 18 | def add_edge(self, src, dest): 19 | '''Add edge in undirected graph''' 20 | self.adj_list[src].append(dest) 21 | self.adj_list[dest].append(src) 22 | 23 | def detect_cycle_util(self, start, parent, visited): 24 | '''Recursive use depth first search to detect cycle in a graph''' 25 | if not visited[start]: 26 | visited[start] = True 27 | 28 | for vertex in self.adj_list[start]: 29 | if not visited[vertex]: 30 | if self.detect_cycle_util(vertex, start, visited, rec_stack): 31 | return True 32 | 33 | elif (vertex != parent): 34 | return True 35 | 36 | return False 37 | 38 | def detect_cycle(self): 39 | '''Detect cycle in a undirected graph''' 40 | visited = [False] * self.V 41 | 42 | for v in range(self.V): 43 | if self.detect_cycle_util(v, -1, visited): 44 | return True 45 | 46 | return False 47 | 48 | def construct_graph(): 49 | graph = Graph(5) 50 | graph.add_edge(0, 1) 51 | graph.add_edge(1, 2) 52 | graph.add_edge(2, 0) 53 | graph.add_edge(0, 3) 54 | graph.add_edge(3, 4) 55 | 56 | return graph 57 | 58 | if __name__ == "__main__": 59 | graph = construct_graph() 60 | print graph.detect_cycle() 61 | -------------------------------------------------------------------------------- /data-structure-questions/graph/detect_cycle_in_undirected_graph_union_find.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | ''' 4 | Program to detect cycle in an undirected graph using union-find algorithm 5 | ''' 6 | 7 | class Edge(object): 8 | def __init__(self, src, dest): 9 | self.src = src 10 | self.dest = dest 11 | 12 | class Graph(object): 13 | def __init__(self, V): 14 | self.V = V 15 | # Keep the list of edges 16 | self.edges = [] 17 | # Store the parent of each node 18 | self.parent = [-1] * self.V 19 | 20 | def add_edge(self, src, dest): 21 | '''Add edges to the graph''' 22 | edge = Edge(src, dest) 23 | self.edges.append(edge) 24 | 25 | def union(self, src, dest): 26 | '''When there is a edge between two vertex put them in the same subset''' 27 | parent = self.get_parent(src) 28 | self.parent[dest] = parent 29 | 30 | def get_parent(self, vertex): 31 | '''Return the parent of a vertex and if none return vertex itself''' 32 | if self.parent[vertex] != -1: 33 | return self.get_parent(self.parent[vertex]) 34 | else: 35 | return vertex 36 | 37 | def detect_cycle(self): 38 | '''Detect cycle in a graph''' 39 | for edge in self.edges: 40 | if self.get_parent(edge.src) != self.get_parent(edge.dest): 41 | self.union(edge.src, edge.dest) 42 | else: 43 | return True 44 | 45 | return False 46 | 47 | def construct_graph(): 48 | graph = Graph(3) 49 | graph.add_edge(0, 1) 50 | graph.add_edge(1, 2) 51 | graph.add_edge(2, 0) 52 | 53 | return graph 54 | 55 | if __name__ == "__main__": 56 | graph = construct_graph() 57 | print graph.detect_cycle() 58 | -------------------------------------------------------------------------------- /data-structure-questions/graph/eulerian_path.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | # Program to detect if a path contains eulerian cycle/path or not. 4 | 5 | '''Conditions: 6 | 1. All vertices with non-zero degree should be connected. 7 | 2. Eulerian path: If zero or two vertices have odd degree and all other vertices have even degree. 8 | Eulerian Cycle: All vertices have even degree. ''' 9 | 10 | import graph 11 | 12 | # use dfs traversal to check connectivity of a graph 13 | def dfs(graph, visited, v): 14 | visited.append(v) 15 | 16 | for i in graph.edges[v]: 17 | if i not in visited: 18 | dfs(graph, visited, i) 19 | 20 | # function to check if the graph is connected or not 21 | def is_connected(graph): 22 | 23 | for i in range(graph.V): 24 | if len(graph.edges[i]) > 0: 25 | break 26 | 27 | # if all the nodes are of zero degree, then it is a connected graph. those nodes will not be part of eulerian 28 | if i == graph.V-1: 29 | return True 30 | 31 | visited = [] 32 | dfs(graph, visited, i) 33 | 34 | if len(visited) == graph.V: 35 | return True 36 | else: 37 | return False 38 | 39 | def is_eulerian(graph): 40 | 41 | if not is_connected(graph): 42 | return False 43 | 44 | count = 0 45 | 46 | for i in graph.edges: 47 | if len(i) % 2 == 1: 48 | count += 1 49 | 50 | if count == 0 or count == 2: 51 | return "Eulerian Path" 52 | else: 53 | return "Eulerian Cycle" 54 | 55 | graph = graph.construct_graph_undirected() 56 | print graph.edges 57 | print is_eulerian(graph) -------------------------------------------------------------------------------- /data-structure-questions/graph/graph_coloring_problem.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | # Given an undirected graph and a number k, determine if the graph can be colored with at most k colors 4 | import graph 5 | 6 | def isSafe(graph, v, i, colors): 7 | 8 | connected_nodes = graph.edges[v] 9 | 10 | for node in connected_nodes: 11 | if colors[node] == i: 12 | return False 13 | return True 14 | 15 | def graph_coloring_util(graph, k, colors, v): 16 | if v == graph.V: 17 | return True 18 | 19 | for i in range(1, k+1): 20 | if colors[v] == -1 and isSafe(graph, v, i, colors): 21 | # Assign color to node 22 | colors[v] = i 23 | 24 | if graph_coloring_util(graph, k, colors, v+1): 25 | return True 26 | 27 | # Backtrack 28 | colors[v] = -1 29 | 30 | return False 31 | 32 | 33 | def graph_coloring(k, graph): 34 | 35 | colors = [-1] * graph.V 36 | 37 | if graph_coloring_util(graph, k, colors, 0): 38 | print colors 39 | return True 40 | else: 41 | return False 42 | 43 | graph = graph.construct_graph_undirected() 44 | print graph_coloring(3, graph) -------------------------------------------------------------------------------- /data-structure-questions/graph/hamiltonian_cycle.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | # Program to find hamiltonian cycle in a graph 4 | 5 | import graph 6 | 7 | def isSafe(graph, path, i): 8 | if i in graph.edges[path[-1]] and i not in path: 9 | return True 10 | return False 11 | 12 | def hamiltonian_cycle_util(graph, path): 13 | if len(path) == graph.V: 14 | if 0 in graph.edges[path[-1]]: 15 | path.append(0) 16 | print path 17 | return True 18 | else: 19 | return False 20 | else: 21 | for i in range(graph.V): 22 | if isSafe(graph, path, i): 23 | path.append(i) 24 | # Recursive look for solution 25 | if hamiltonian_cycle_util(graph, path): 26 | return True 27 | # Backtrack 28 | del path[-1] 29 | 30 | return False 31 | 32 | graph = graph.construct_graph_undirected() 33 | 34 | print hamiltonian_cycle_util(graph, [0]) -------------------------------------------------------------------------------- /data-structure-questions/graph/iterative_depth_first_traversal.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | ''' 4 | Iterative depth first traversal 5 | ''' 6 | 7 | class Graph(object): 8 | def __init__(self, V): 9 | self.V = V 10 | self.adj_list = [[] for i in range(V)] 11 | 12 | def add_edge(self, src, dest): 13 | self.adj_list[src].append(dest) 14 | 15 | def dfs_util(self, start, visited): 16 | '''Print depth first traversal of a graph''' 17 | stack = [start] 18 | 19 | while stack: 20 | s = stack.pop(-1) 21 | if not visited[s]: 22 | visited[s] = 1 23 | print s 24 | 25 | for vertex in self.adj_list[s]: 26 | if not visited[vertex]: 27 | stack.append(vertex) 28 | 29 | def dfs(self): 30 | visited = [0] * self.V 31 | for i in range(self.V): 32 | if not visited[i]: 33 | self.dfs_util(i, visited) 34 | 35 | def construct_graph(): 36 | graph = Graph(5) 37 | graph.add_edge(1, 0) 38 | graph.add_edge(0, 2) 39 | graph.add_edge(2, 1) 40 | graph.add_edge(0, 3) 41 | graph.add_edge(1, 4) 42 | 43 | return graph 44 | 45 | if __name__ == "__main__": 46 | graph = construct_graph() 47 | graph.dfs() 48 | -------------------------------------------------------------------------------- /data-structure-questions/graph/strongly_connected_components.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | # Program to find strongly connected components of a graph. 4 | 5 | import graph 6 | 7 | def transpose(g1): 8 | 9 | gr = graph.Graph(g1.V, [None]*g1.V) 10 | gr.edges = [[] for i in gr.edges] 11 | 12 | for i in range(g1.V): 13 | for j in g1.edges[i]: 14 | gr.edges[j].append(i) 15 | 16 | return gr 17 | 18 | def dfs(g, start, visited): 19 | 20 | visited.append(start) 21 | print start 22 | 23 | for i in g.edges[start]: 24 | if i not in visited: 25 | dfs(g, i, visited) 26 | 27 | 28 | def topological_sort(g1, start, visited, stack): 29 | visited.append(start) 30 | 31 | for i in g1.edges[start]: 32 | if i not in visited: 33 | topological_sort(g1, i, visited, stack) 34 | 35 | stack.append(start) 36 | 37 | def SCC(g1): 38 | 39 | visited = [] 40 | stack = [] 41 | 42 | for i in range(g1.V): 43 | if i not in visited: 44 | topological_sort(g1, i, visited, stack) 45 | 46 | # transpose the current graph 47 | gr = transpose(g1) 48 | 49 | # apply dfs on the transposed graph 50 | visited = [] 51 | while (stack): 52 | 53 | s = stack[-1] 54 | del stack[-1] 55 | 56 | if s not in visited: 57 | print "Next SCC" 58 | dfs(gr, s, visited) 59 | 60 | g1 = graph.construct_graph() 61 | 62 | SCC(g1) 63 | -------------------------------------------------------------------------------- /data-structure-questions/graph/transitive_closure_of_a_graph.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | ''' 4 | Given a directed graph, find out if a vertex v is reachable from another vertex u for all vertex pairs (u, v) 5 | in the given graph. Here reachable mean that there is a path from vertex u to v. The reach-ability matrix is 6 | called transitive closure of a graph. 7 | ''' 8 | 9 | class Graph(object): 10 | def __init__(self, V): 11 | self.V = V 12 | self.adj_list = {i: [] for i in range(self.V)} 13 | 14 | def add_edge(self, src, dest): 15 | self.adj_list[src].append(dest) 16 | 17 | def dfs_util(self, start, vertex, closure): 18 | '''Print depth first traversal of a graph''' 19 | closure[start][vertex] = 1 20 | 21 | for v in self.adj_list[vertex]: 22 | if not closure[start][v]: 23 | self.dfs_util(start, v, closure) 24 | 25 | def transitive_closure(self): 26 | '''Return transitive closure of the vertices in a graph''' 27 | closure = [[0] * self.V for i in range(self.V)] 28 | 29 | for i in range(self.V): 30 | self.dfs_util(i, i, closure) 31 | 32 | print closure 33 | 34 | def construct_graph(): 35 | graph = Graph(4) 36 | graph.add_edge(0, 1) 37 | graph.add_edge(0, 2) 38 | graph.add_edge(1, 2) 39 | graph.add_edge(2, 0) 40 | graph.add_edge(2, 3) 41 | graph.add_edge(3, 3) 42 | 43 | return graph 44 | 45 | if __name__ == "__main__": 46 | graph = construct_graph() 47 | graph.transitive_closure() 48 | -------------------------------------------------------------------------------- /data-structure-questions/linked_list/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/charulagrl/data-structures-and-algorithms/79ccc0aa34d9e3eb508bfe64d1fcee9dd1ea5e98/data-structure-questions/linked_list/__init__.py -------------------------------------------------------------------------------- /data-structure-questions/linked_list/add_two_numbers_in_linked_list.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | # Program to add two numbers 4 | 5 | import initialize 6 | import linked_list 7 | 8 | def add(head1, head2): 9 | 10 | if not head1 or not head2: 11 | return "Linked List are empty" 12 | 13 | curr = None 14 | carry = 0 15 | 16 | while (head1 or head2): 17 | total_sum = 0 18 | 19 | if head1: 20 | total_sum += head1.data 21 | 22 | if head2: 23 | total_sum += head2.data 24 | 25 | 26 | sum = carry + (total_sum % 10) 27 | carry = total_sum / 10 28 | 29 | temp = linked_list.Node(sum) 30 | 31 | if curr: 32 | curr.nextnode = temp 33 | curr = curr.nextnode 34 | else: 35 | ll = linked_list.LinkedList(temp) 36 | curr = ll.head 37 | 38 | if head1: 39 | head1 = head1.nextnode 40 | if head2: 41 | head2 = head2.nextnode 42 | 43 | if carry: 44 | curr.nextnode = linked_list.Node(carry) 45 | 46 | return ll 47 | 48 | 49 | lList1 = initialize.initialize_linked_list_by_array([8, 0, 0]) 50 | lList2 = initialize.initialize_linked_list_by_array([8, 0, 0]) 51 | 52 | ll = add(lList1.head, lList2.head) 53 | 54 | ll.print_list() -------------------------------------------------------------------------------- /data-structure-questions/linked_list/alternate_merge.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | # Program to alternately merge the elements of two given linked lists into a single list. 4 | 5 | import initialize 6 | 7 | def alternate_merge(head1, head2): 8 | 9 | if not head1 and not head2: 10 | return 11 | 12 | if not head2: 13 | return head1 14 | 15 | if not head1: 16 | return head2 17 | 18 | temp1 = head1 19 | temp2 = head2 20 | prev1 = None 21 | 22 | while (temp1 and temp2): 23 | 24 | prev1 = temp1 25 | next1 = temp1.nextnode 26 | 27 | prev2 = temp2 28 | next2 = temp2.nextnode 29 | 30 | prev1.nextnode = prev2 31 | prev2.nextnode = next1 32 | 33 | temp2 = next2 34 | temp1 = next1 35 | 36 | if temp2: 37 | prev2.nextnode = temp2 38 | 39 | return head1 40 | 41 | lList1 = initialize.initialize_linked_list() 42 | lList2 = initialize.initialize_linked_list_by_array([4, 5, 6, 7, 8, 9]) 43 | 44 | head1 = alternate_merge(lList1.head, lList2.head) 45 | 46 | while (head1): 47 | print head1.data 48 | head1 = head1.nextnode -------------------------------------------------------------------------------- /data-structure-questions/linked_list/check_if_linklist_is_palindrome.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | # Program to verify if a link list is a palindrome 4 | 5 | import initialize 6 | 7 | def compare(head, head2): 8 | if not head and not head2: 9 | return True 10 | 11 | if not head or not head2: 12 | return False 13 | 14 | while head and head2: 15 | if head.data != head2.data: 16 | return False 17 | head = head.nextnode 18 | head2 = head2.nextnode 19 | 20 | return True 21 | 22 | def reverse(head): 23 | curr = head 24 | prev = None 25 | 26 | while curr: 27 | next = curr.nextnode 28 | curr.nextnode = prev 29 | prev = curr 30 | curr = next 31 | 32 | return prev 33 | 34 | def check_palindrome(head): 35 | if not head or not head.nextnode: 36 | return True 37 | 38 | fast_ptr = head 39 | slow_ptr = head 40 | while fast_ptr and fast_ptr.nextnode: 41 | prev_slow_ptr = slow_ptr 42 | slow_ptr = slow_ptr.nextnode 43 | fast_ptr = fast_ptr.nextnode.nextnode 44 | 45 | if fast_ptr: 46 | mid_point = slow_ptr 47 | slow_ptr = slow_ptr.nextnode 48 | 49 | second_half = slow_ptr 50 | prev_slow_ptr.next = None 51 | # Reverse second half of linked list 52 | head_second_half = reverse(second_half) 53 | # Compare two lists if they are equal or not 54 | result = compare(head, head_second_half) 55 | 56 | head_second_half = reverse(head_second_half) 57 | 58 | if mid_point: 59 | slow_ptr.nextnode = mid_point 60 | mid_point.nextnode = head_second_half 61 | else: 62 | prev_slow_ptr.nextnode = second_half 63 | 64 | return result 65 | 66 | head = initialize.initialize_linked_list() 67 | print check_palindrome(head) 68 | -------------------------------------------------------------------------------- /data-structure-questions/linked_list/common_intersection_of_two_linked_list.py: -------------------------------------------------------------------------------- 1 | # * coding: UTF 8 * 2 | 3 | # Program to find intersection of two sorted linked list 4 | 5 | import initialize 6 | 7 | class Node(object): 8 | 9 | def __init__(self, data): 10 | 11 | self.data = data 12 | self.nextnode = None 13 | 14 | def find_intersection_of_two_linked_list(head1, head2, head): 15 | if not head1 or not head2: 16 | raise ValueError ("Linked list are empty") 17 | 18 | current1 = head1 19 | current2 = head2 20 | 21 | while current1 and current2: 22 | if current1.data < current2.data: 23 | current1 = current1.nextnode 24 | elif current2.data < current1.data: 25 | current2 = current2.nextnode 26 | elif current1.data == current2.data: 27 | temp = Node(current1.data) 28 | if head == None: 29 | head = temp 30 | tail = head 31 | else: 32 | tail.nextnode = temp 33 | tail = tail.nextnode 34 | current1 = current1.nextnode 35 | current2 = current2.nextnode 36 | return head 37 | 38 | lList1 = initialize.initialize_linked_list_by_array([6, 4, 3, 2, 1]) 39 | lList2 = initialize.initialize_linked_list_by_array([8, 6, 4, 2]) 40 | 41 | head = find_intersection_of_two_linked_list(lList1.head, lList2.head, None) 42 | 43 | while (head): 44 | print head.data 45 | head = head.nextnode -------------------------------------------------------------------------------- /data-structure-questions/linked_list/delete_alternate_nodes.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | # Program to delete alternate nodes in a linked list 4 | 5 | import initialize 6 | 7 | def delete_alternate(head): 8 | 9 | if not head: 10 | return 11 | 12 | elif not head.nextnode: 13 | return head 14 | 15 | else: 16 | 17 | curr = head 18 | 19 | while (curr and curr.nextnode): 20 | 21 | curr.nextnode = curr.nextnode.nextnode 22 | curr = curr.nextnode 23 | 24 | return head 25 | 26 | lList = initialize.initialize_linked_list() 27 | 28 | head = delete_alternate(lList.head) 29 | 30 | while (head): 31 | print head.data 32 | head = head.nextnode -------------------------------------------------------------------------------- /data-structure-questions/linked_list/delete_m_after_n.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | import initialize 4 | 5 | # Program to delete m nodes after n nodes 6 | 7 | def delete_m_after_n(head, m, n): 8 | count_n = 0 9 | curr = head 10 | 11 | while ((count_n < n-1) and (curr != None)): 12 | curr = curr.nextnode 13 | count_n += 1 14 | 15 | temp = curr 16 | 17 | count = 0 18 | 19 | while count < m and curr != None: 20 | curr = curr.nextnode 21 | count += 1 22 | 23 | temp.nextnode = curr.nextnode 24 | 25 | 26 | if __name__=="__main__": 27 | ll = initialize.initialize_linked_list() 28 | 29 | delete_m_after_n(ll.head, 2, 3) 30 | 31 | ll.print_list() 32 | -------------------------------------------------------------------------------- /data-structure-questions/linked_list/detect_and_remove_loop.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | # Program to detect and remove loop in a linked list 4 | 5 | import initialize 6 | 7 | def detect_loop(head): 8 | 9 | if head == None: 10 | return 11 | 12 | slw_ptr = head 13 | fast_ptr = head 14 | 15 | while (slw_ptr and fast_ptr and fast_ptr.nextnode): 16 | slw_ptr = slw_ptr.nextnode 17 | fast_ptr = fast_ptr.nextnode.nextnode 18 | if slw_ptr == fast_ptr: 19 | remove_loop(head, slw_ptr) 20 | 21 | return -1 22 | 23 | 24 | def remove_loop(head, ptr): 25 | if not ptr: 26 | return 27 | 28 | curr = head 29 | 30 | while (curr): 31 | temp = ptr 32 | 33 | while (curr and temp.nextnode != ptr and temp.nextnode != curr): 34 | temp = temp.nextnode 35 | 36 | if temp.nextnode == curr: 37 | temp.nextnode = None 38 | print "Loop removed at " + str(curr.data) 39 | return 40 | 41 | curr = curr.nextnode 42 | 43 | 44 | l_list = initialize.initialize_linked_list() 45 | 46 | head = l_list.head 47 | 48 | head.nextnode.nextnode.nextnode.nextnode.nextnode.nextnode.nextnode.nextnode.nextnode = head.nextnode.nextnode.nextnode 49 | 50 | detect_loop(head) 51 | 52 | l_list.print_list() 53 | -------------------------------------------------------------------------------- /data-structure-questions/linked_list/find_intersection_of_two_linked_list.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | # Program to find intersection of two sorted linked list 4 | 5 | import initialize 6 | 7 | def find_intersection_of_two_linked_list(head1, head2): 8 | if not head1 or not head2: 9 | raise ValueError ("Linked list are empty") 10 | 11 | current1 = head1 12 | current2 = head2 13 | 14 | while current1 and current2: 15 | if current1.data < current2.data: 16 | current1 = current1.nextnode 17 | elif current2.data < current1.data: 18 | current2 = current2.nextnode 19 | elif current1.data == current2.data: 20 | return "Linked list intersect at %s"%str(current1.data) 21 | 22 | 23 | return 'Lists do not intersect' 24 | 25 | 26 | lList1 = initialize.initialize_linked_list_by_array([30,15,9,6, 3]) 27 | lList2 = initialize.initialize_linked_list_by_array([30, 15, 10]) 28 | 29 | print find_intersection_of_two_linked_list(lList1.head, lList2.head) -------------------------------------------------------------------------------- /data-structure-questions/linked_list/initialize.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | # Program to initialize linked list 4 | import linked_list as ll 5 | 6 | # Initialize the following linked listaz 7 | # 4 -> 5 -> 13 -> 6 -> 9 -> 41 -> 8 -> 27 -> 33 8 | def initialize_linked_list(): 9 | lList = ll.LinkedList() 10 | lList.insert(4) 11 | lList.insert(5) 12 | lList.insert(13) 13 | lList.insert(6) 14 | lList.insert(9) 15 | lList.insert(41) 16 | lList.insert(8) 17 | lList.insert(4) 18 | lList.insert(33) 19 | 20 | lList.print_list() 21 | return lList 22 | 23 | # Initialize a linked list by taking elements from an array 24 | def initialize_linked_list_by_array(elements): 25 | 26 | lList = ll.LinkedList() 27 | 28 | 29 | for element in elements: 30 | lList.insert(element) 31 | 32 | lList.print_list() 33 | 34 | return lList.head 35 | -------------------------------------------------------------------------------- /data-structure-questions/linked_list/kth_node_from_end.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | # Program to print kth node from end 4 | 5 | import initialize 6 | 7 | # --------*********** Approach - 1 *************--------------- 8 | # Find kth node by calculating the size first 9 | # Then from staring the node will be at (size-k) location 10 | # Iterating to (size - k) location 11 | def kth_node_from_end_1(ll, k): 12 | if ll.head is None: 13 | return 14 | 15 | size = ll.size() 16 | 17 | if k > size: 18 | return 19 | 20 | current = ll.head 21 | pos = 0 22 | 23 | while(pos < (size - k)): 24 | current = current.nextnode 25 | pos += 1 26 | 27 | print "The element at position %d is %d"%(k, current.data) 28 | 29 | 30 | # --------*********** Approach - 2 *************--------------- 31 | 32 | # Find kth node from end by using a fast pointer 33 | # Take a fast pointer which first is moved till kth location from beginning 34 | # Then both pointers are iterated till the last pointer and main pointer is returned. 35 | def kth_node_from_end_2(head, k): 36 | 37 | if head is None: 38 | return 39 | 40 | node_ptr = head 41 | main_ptr = head 42 | 43 | count = 0 44 | 45 | while (count < k): 46 | node_ptr = node_ptr.nextnode 47 | count += 1 48 | 49 | while (node_ptr): 50 | main_ptr = main_ptr.nextnode 51 | node_ptr = node_ptr.nextnode 52 | 53 | print "The element at position %d is %d"%(k, main_ptr.data) 54 | 55 | # Initialize the linked list 56 | # 33 27 8 41 9 6 13 5 4 57 | lList = initialize.initialize_linked_list() 58 | 59 | # Call the kth element from end function 60 | kth_node_from_end_1(lList, 4) 61 | kth_node_from_end_2(lList.head, 5) 62 | -------------------------------------------------------------------------------- /data-structure-questions/linked_list/reverse_alternate_k_nodes.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | # Program to reverse alternate k nodes in a linked list 4 | import initialize 5 | 6 | def reverse_alternate_k_nodes(head, k): 7 | 8 | count = 0 9 | prev = None 10 | curr = head 11 | 12 | # reverse first k nodes in link list 13 | while count < k and curr: 14 | next = curr.nextnode 15 | curr.nextnode = prev 16 | prev = curr 17 | curr = next 18 | count += 1 19 | 20 | # head will point to k node. So next of it will point to the k+1 node 21 | if head: 22 | head.nextnode = curr 23 | 24 | # don't want to reverse next k nodes. 25 | count = 0 26 | while count < k-1 and curr: 27 | curr = curr.nextnode 28 | count += 1 29 | 30 | # Recursively call the same method if there are more elements in the link list 31 | if curr: 32 | curr.nextnode = reverse_alternate_k_nodes(curr.nextnode, k) 33 | 34 | return prev 35 | 36 | head = initialize.initialize_linked_list() 37 | 38 | head = reverse_alternate_k_nodes(head, 2) 39 | print "After reversing the alternate k nodes in linked list" 40 | 41 | while head: 42 | print head.data 43 | head = head.nextnode 44 | -------------------------------------------------------------------------------- /data-structure-questions/linked_list/rotate_linked_list.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | import initialize 4 | 5 | # Program to rotate a linked list 6 | 7 | def rotate(head): 8 | 9 | if not head: 10 | return 11 | 12 | temp = head 13 | 14 | while (temp.nextnode): 15 | temp = temp.nextnode 16 | 17 | node = head 18 | head = head.nextnode 19 | temp.nextnode = node 20 | temp.nextnode.nextnode = None 21 | 22 | return head 23 | 24 | lList = initialize.initialize_linked_list() 25 | 26 | head = rotate(lList.head) 27 | 28 | while (head): 29 | print head.data 30 | head = head.nextnode -------------------------------------------------------------------------------- /data-structure-questions/linked_list/swap_alternate_nodes.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | # Program to swap alternate nodes in a linked list 4 | 5 | import initialize 6 | 7 | def swap_alternate_nodes(head): 8 | if not head: 9 | return 10 | 11 | else: 12 | curr = head 13 | prev = None 14 | while (curr and curr.nextnode): 15 | a = curr 16 | b = curr.nextnode 17 | 18 | next = b.nextnode 19 | 20 | if prev == None: 21 | head = b 22 | else: 23 | prev.nextnode = b 24 | b.nextnode = a 25 | a.nextnode = next 26 | 27 | prev = curr 28 | curr = curr.nextnode 29 | 30 | return head 31 | 32 | lList = initialize.initialize_linked_list() 33 | 34 | head = swap_alternate_nodes(lList.head) 35 | 36 | while (head): 37 | print head.data 38 | head = head.nextnode -------------------------------------------------------------------------------- /data-structure-questions/linked_list/swap_k_beginning_and_k_end.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | # Program to swap kth node from start with kth node from end 4 | 5 | import initialize 6 | 7 | def swap(lList, k): 8 | 9 | if not lList.head: 10 | raise ValueError("List is empty") 11 | 12 | count = lList.size() 13 | 14 | if 2*k-1 == count: 15 | return 16 | 17 | x = lList.head 18 | x_prev = None 19 | 20 | for i in range(k-1): 21 | x_prev = x 22 | x = x.nextnode 23 | 24 | y = lList.head 25 | y_prev = None 26 | 27 | for i in range(count-k): 28 | y_prev = y 29 | y = y.nextnode 30 | 31 | if x_prev: 32 | x_prev.nextnode = y 33 | 34 | if y_prev: 35 | y_prev.nextnode = x 36 | 37 | temp = x.nextnode 38 | x.nextnode = y.nextnode 39 | y.nextnode = temp 40 | 41 | if k == 1: 42 | lList.head = y 43 | if k == count: 44 | lList.head = x 45 | 46 | return lList.head 47 | 48 | lList = initialize.initialize_linked_list() 49 | 50 | head = swap(lList, 1) 51 | 52 | while (head): 53 | print head.data 54 | head = head.nextnode 55 | -------------------------------------------------------------------------------- /data-structure-questions/string/anagram.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | # @param A : tuple of strings 3 | # @return a list of list of integers 4 | def anagrams(self, A): 5 | word_list = [] 6 | indices = [] 7 | for i, word in enumerate(A): 8 | word = ''.join(sorted(word)) 9 | 10 | if word in word_list: 11 | index = word_list.index(word) 12 | indices[index].append(i+1) 13 | else: 14 | word_list.append(word) 15 | indices.append([i+1]) 16 | 17 | return indices 18 | 19 | A = [ "abbbaabbbabbbbabababbbbbbbaabaaabbaaababbabbabbaababbbaaabbabaabbaabbabbbbbababbbababbbbaabababba", 20 | "abaaabbbabaaabbbbabaabbabaaaababbbbabbbaaaabaababbbbaaaabbbaaaabaabbaaabbaabaaabbabbaaaababbabbaa", 21 | "babbabbaaabbbbabaaaabaabaabbbabaabaaabbbbbbabbabababbbabaabaabbaabaabaabbaabbbabaabbbabaaaabbbbab", 22 | "bbbabaaabaaaaabaabaaaaaaabbabaaaabbababbabbabbaabbabaaabaabbbabbaabaabaabaaaabbabbabaaababbaababb", 23 | "abbbbbbbbbbbbabaabbbbabababaabaabbbababbabbabaaaabaabbabbaaabbaaaabbaabbbbbaaaabaaaaababababaabab", 24 | "aabbbbaaabbaabbbbabbbbbaabbababbbbababbbabaabbbbbbababaaaabbbabaabbbbabbbababbbaaabbabaaaabaaaaba", 25 | "abbaaababbbabbbbabababbbababbbaaaaabbbbbbaaaabbaaabbbbbbabbabbabbaabbbbaabaabbababbbaabbbaababbaa", 26 | "aabaaabaaaaaabbbbaabbabaaaabbaababaaabbabbaaaaababaaabaabbbabbababaabababbaabaababbaabbabbbaaabbb" ] 27 | 28 | s = Solution() 29 | print s.anagrams(A) 30 | -------------------------------------------------------------------------------- /data-structure-questions/string/check_if_string_formed_from_interleavings.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | # Program to Check if a string is formed by interleaving of two strings 4 | 5 | def check(str, str1, str2, m, n, i): 6 | 7 | if m == 0 and n == 0 and i == 0: 8 | return True 9 | else: 10 | if m > 0 and i > 0 and str[0] == str1[0]: 11 | return check(str[1:], str1[1:], str2, m-1, n, i-1) 12 | 13 | elif n > 0 and i > 0 and str[0] == str2[0]: 14 | return check(str[1:], str1, str2[1:], m, n-1, i-1) 15 | 16 | else: 17 | return False 18 | 19 | a = list("CABD") 20 | b = list("AB") 21 | c = list("CD") 22 | 23 | print check(a, b, c, len(b), len(c), len(a)) 24 | 25 | -------------------------------------------------------------------------------- /data-structure-questions/string/check_if_strings_are_anagrams.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | # Program to check if two strings are anagrams of each other 4 | 5 | def count_characters(str1, count_dict): 6 | 7 | for i in str1: 8 | if i not in count_dict: 9 | count_dict[i] = 1 10 | else: 11 | count_dict[i] += 1 12 | 13 | return count_dict 14 | 15 | def check_anagrams(str1, str2): 16 | 17 | count_dict = count_characters(str1, {}) 18 | 19 | for i in str2: 20 | if i in count_dict and count_dict[i] > 0: 21 | count_dict[i] -= 1 22 | else: 23 | return False 24 | 25 | for i in count_dict: 26 | if count_dict[i] != 0: 27 | return False 28 | 29 | return True 30 | 31 | print check_anagrams("aabbrd", "rabab") -------------------------------------------------------------------------------- /data-structure-questions/string/interleavings_of_two_strings.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | # Program to print all interleavings of two strings 4 | 5 | def print_all_interleavings(str1, str2, str, m, n, i): 6 | 7 | if m == 0 and n == 0: 8 | print "".join(str) 9 | 10 | else: 11 | 12 | if m != 0: 13 | str[i] = str1[0] 14 | print_all_interleavings(str1[1:], str2, str, m-1, n, i+1) 15 | 16 | if n != 0: 17 | str[i] = str2[0] 18 | print_all_interleavings(str1, str2[1:], str, m, n-1, i+1) 19 | 20 | 21 | a = "AB" 22 | b = "CDE" 23 | 24 | c = [""] * (len(a)+len(b)) 25 | 26 | print_all_interleavings(list(a), list(b), c, len(a), len(b), 0) -------------------------------------------------------------------------------- /data-structure-questions/string/kmp_pattern_searching.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | # KMP algorithm to search for pattern in a string 4 | 5 | def KMPSearch(pattern, text): 6 | 7 | lps = computeLPS(pattern) 8 | 9 | i = 0 10 | j = 0 11 | while i < len(text): 12 | if pattern[j] == text[i]: 13 | i += 1 14 | j += 1 15 | 16 | if j == len(pattern): 17 | print "Pattern found at %s"%str(i-j) 18 | j = lps[j-1] 19 | 20 | elif i < len(text) and pattern[j] != text[i]: 21 | if j != 0: 22 | j = lps[j-1] 23 | else: 24 | i += 1 25 | 26 | # Compute longest proper suffix for pattern 27 | def computeLPS(pattern): 28 | 29 | lps = [0]*len(pattern) 30 | 31 | lps[0] = 0 32 | i = 1 33 | l = 0 34 | 35 | while i < len(pattern): 36 | if pattern[i] == pattern[l]: 37 | l += 1 38 | lps[i] = l 39 | i += 1 40 | 41 | else: 42 | if l != 0: 43 | l = lps[l-1] 44 | 45 | else: 46 | lps[i] = 0 47 | i += 1 48 | 49 | return lps 50 | 51 | S = "AABAACAADAABAAABAA" 52 | P = "AABA" 53 | 54 | KMPSearch(P, S) -------------------------------------------------------------------------------- /data-structure-questions/string/longest_common_subsequence.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | # Program to print longest common subsequence 4 | 5 | def longest_common_subsequence(str1, str2): 6 | maxLength = 0 7 | 8 | longest_substring = [] 9 | 10 | for i in range(len(str1)+1): 11 | longest_substring.append([0]*(len(str2)+1)) 12 | 13 | for i in range(len(str1)+1): 14 | for j in range(len(str2)+1): 15 | if i == 0 and j == 0: 16 | longest_substring[i][j] = 0 17 | if str1[i-1] == str2[j-1]: 18 | longest_substring[i][j] = longest_substring[i-1][j-1] + 1 19 | else: 20 | longest_substring[i][j] = max(longest_substring[i-1][j], longest_substring[i][j-1]) 21 | 22 | 23 | return longest_substring[len(str1)][len(str2)] 24 | 25 | 26 | a = list("ABCDGH") 27 | b = list("AEDFHR") 28 | 29 | print longest_common_subsequence(a, b) -------------------------------------------------------------------------------- /data-structure-questions/string/longest_common_substring.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | # Program to find longest common substring between two strings 4 | 5 | def longest_common_substring(str1, str2): 6 | 7 | maxLength = 0 8 | 9 | longest_substring = [] 10 | 11 | for i in range(len(str1)+1): 12 | longest_substring.append([0]*(len(str2)+1)) 13 | 14 | for i in range(len(str1)+1): 15 | for j in range(len(str2)+1): 16 | 17 | if i == 0 or j == 0: 18 | longest_substring[i][j] = 0 19 | 20 | if str1[i-1] == str2[j-1]: 21 | longest_substring[i][j] = 1 + longest_substring[i-1][j-1] 22 | if longest_substring[i][j] > maxLength: 23 | maxLength = longest_substring[i][j] 24 | 25 | else: 26 | longest_substring[i][j] = 0 27 | print maxLength 28 | a = "SGeeksfor" 29 | b = "Geeks" 30 | 31 | longest_common_substring(a, b) -------------------------------------------------------------------------------- /data-structure-questions/string/longest_palindrome_substring.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 2 | 3 | # Program to find the length of longest palindrome substring 4 | 5 | def longest_palindrome(s): 6 | maxLength = 1 7 | 8 | start = 0 9 | 10 | # consider one by one every point as its center point of even and odd substring 11 | for k in range(1, len(s)): 12 | 13 | # considering all even length substrings with center as k and k-1 14 | low = k-1 15 | high = k 16 | 17 | while low >= 0 and high < len(s) and s[low] == s[high]: 18 | if high-low+1 > maxLength: 19 | maxLength = high-low+1 20 | start = low 21 | # expand the substring on both left and right side 22 | high += 1 23 | low -= 1 24 | 25 | # consider all odd length strings with center as k 26 | low = k-1 27 | high = k+1 28 | 29 | while low >= 0 and high < len(s) and s[low] == s[high]: 30 | if high-low+1 > maxLength: 31 | maxLength = high-low+1 32 | start = low 33 | 34 | high += 1 35 | low -= 1 36 | 37 | 38 | longest_substring = "".join(s[start:start+maxLength]) 39 | 40 | print "longest palindromic substring", longest_substring 41 | 42 | s = list("forgeeksskeegrof") 43 | 44 | longest_palindrome(s) 45 | -------------------------------------------------------------------------------- /data-structure-questions/string/min_edit_distance.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | # Program to calculate edit distance between two strings 4 | 5 | # Recursive solution to implement edit distance 6 | def edit_distance(str1, str2, m, n): 7 | if m == 0 and n == 0: 8 | return 0 9 | if m == 0: 10 | return n 11 | if n == 0: 12 | return m 13 | 14 | left = edit_distance(str1, str2, m-1, n) + 1 15 | right = edit_distance(str1, str2, m, n-1) + 1 16 | 17 | corner = edit_distance(str1, str2, m-1, n-1) + (str1[m-1] != str2[n-1]) 18 | 19 | return min(left, min(right, corner)) 20 | 21 | a = list("abc") 22 | b = list("abcd") 23 | print edit_distance(a, b, len(a), len(b)) -------------------------------------------------------------------------------- /data-structure-questions/string/most_occuring_element_in_string.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | # Program to find most/least occuring element in a string 4 | 5 | def count(s, count_dict): 6 | 7 | for i in s: 8 | if i not in count_dict: 9 | count_dict[i] = 1 10 | else: 11 | count_dict[i] += 1 12 | 13 | max_value = 0 14 | max_element = 0 15 | for i in count_dict: 16 | if count_dict[i] > max_value: 17 | max_value = count_dict[i] 18 | max_element = i 19 | 20 | return max_element 21 | 22 | s = "aabbccaaabbmmmlllssssaaa" 23 | 24 | count_dict = {} 25 | 26 | print count(s, count_dict) 27 | -------------------------------------------------------------------------------- /data-structure-questions/string/pattern_matching/kmp_algorithm.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | ''' 4 | Given a text txt[0..n-1] and a pattern pat[0..m-1], write a function search(char pat[], char txt[]) that prints 5 | all occurrences of pat[] in txt[]. You may assume that n > m. 6 | ''' 7 | 8 | 9 | def genertate_lps(pattern): 10 | lps = [0] * len(pattern) 11 | m = len(pattern) 12 | i = 1 13 | length = 0 14 | while (i < m): 15 | if pattern[i] == pattern[length]: 16 | length += 1 17 | lps[i] = length 18 | i += 1 19 | 20 | else: 21 | if length: 22 | length = lps[length-1] 23 | else: 24 | lps[i] = 0 25 | i += 1 26 | 27 | return lps 28 | 29 | def match_pattern(pattern, txt): 30 | 31 | lps = genertate_lps(pattern) 32 | pattern_length = len(pattern) 33 | txt_length = len(txt) 34 | i = 0 35 | j = 0 36 | while(i < txt_length): 37 | if j < pattern_length and pattern[j] == txt[i]: 38 | j += 1 39 | i += 1 40 | 41 | if j == pattern_length: 42 | print "Pattern matched at index ", i-j 43 | j = lps[j-1] 44 | 45 | elif i < txt_length and pattern[j] != txt[i]: 46 | if j != 0: 47 | j = lps[j-1] 48 | else: 49 | i = i+1 50 | 51 | def test_kmp(): 52 | txt = "AAAAABAAABA" 53 | pattern = "AAAA" 54 | 55 | match_pattern(pattern, txt) 56 | 57 | if __name__ == "__main__": 58 | test_kmp() -------------------------------------------------------------------------------- /data-structure-questions/string/pattern_matching/naive_pattern_search.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | ''' 4 | Given a text txt[0..n-1] and a pattern pat[0..m-1], write a function search(char pat[], char txt[]) that prints all 5 | occurrences of pat[] in txt[]. You may assume that n > m. 6 | 7 | Worst case complexity: O(m * (n-m+1)) 8 | ''' 9 | 10 | def check_for_rest(txt, i, pattern): 11 | '''Check if the all the letters of the pattern are present in the txt starting at index i''' 12 | for j, s in enumerate(pattern): 13 | if not txt[i+j] == s: 14 | return False 15 | 16 | return True 17 | 18 | def search_pattern(str, pattern): 19 | '''Search for the pattern in the string and return all the positions where it is found''' 20 | found_at = [] 21 | pattern_len = len(pattern) 22 | string_len = len(str) 23 | for i in range(string_len-pattern_len+1): 24 | # If first letter matches match the entire pattern 25 | if str[i] == pattern[0]: 26 | if check_for_rest(str, i, pattern): 27 | found_at.append(i) 28 | 29 | return found_at 30 | 31 | 32 | def pattern_test(): 33 | 34 | txt = "AABAACAADAABAAABAA" 35 | pattern = "AABA" 36 | 37 | assert search_pattern(txt, pattern) == [0, 9, 13] 38 | 39 | if __name__ == "__main__": 40 | pattern_test() -------------------------------------------------------------------------------- /data-structure-questions/string/remove_duplicates.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | # Program to remove duplicates from string 4 | 5 | def remove_duplicates(str1): 6 | 7 | scanned_elements = [] 8 | 9 | for i in range(len(str1)): 10 | if str1[i] not in scanned_elements: 11 | scanned_elements.append(str1[i]) 12 | 13 | return "".join(scanned_elements) 14 | 15 | a = list("ddcvvwwfaadsffsqq") 16 | 17 | print remove_duplicates(a) 18 | -------------------------------------------------------------------------------- /data-structure-questions/string/sentence_reverse.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 2 | 3 | ''' 4 | You are given an array of characters arr that consists of sequences of characters separated by space characters. 5 | Each space-delimited sequence of characters defines a word. 6 | 7 | Implement a function reverseWords that reverses the order of the words in the array in the most efficient manner. 8 | ''' 9 | 10 | arr = [ 'p', 'e', 'r', 'f', 'e', 'c', 't', ' ', 11 | 'm', 'a', 'k', 'e', 's', ' ', 12 | 'p', 'r', 'a', 'c', 't', 'i', 'c', 'e' ] 13 | 14 | def reverse(arr, st, end): 15 | while st < end: 16 | arr[st], arr[end] = arr[end], arr[st] 17 | end -= 1 18 | st += 1 19 | 20 | def reverse_arr(arr): 21 | arr = arr[::-1] # this will reverse the string and copy its element to a new string 22 | st_index = 0 23 | length = len(arr) 24 | 25 | for i, val in enumerate(arr): 26 | if val == ' ': 27 | end_index = i-1 28 | reverse(arr, st_index, end_index) 29 | st_index = end_index + 2 30 | 31 | if i == length - 1: 32 | reverse(arr, st_index, length-1) 33 | 34 | return arr 35 | 36 | print reverse_arr(arr) 37 | print reverse_arr([' ', ' ', ' ']) 38 | -------------------------------------------------------------------------------- /data_structures/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/charulagrl/data-structures-and-algorithms/79ccc0aa34d9e3eb508bfe64d1fcee9dd1ea5e98/data_structures/__init__.py -------------------------------------------------------------------------------- /data_structures/abstract_syntax_tree.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | '''Program to create abstract syntax tree''' 4 | 5 | class Node(object): 6 | def __init__(self, operation=None, value=None, left=None, right=None): 7 | self.operation = operation 8 | self.value = value 9 | self.left = left 10 | self.right = right 11 | 12 | 13 | def parse_tree(root): 14 | if root is None: 15 | return 16 | 17 | if root.value is not None: 18 | return root.value 19 | 20 | else: 21 | left = parse_tree(root.left) 22 | right = parse_tree(root.right) 23 | 24 | if root.operation is not None: 25 | if root.operation == "*": 26 | return left * right 27 | elif root.operation == "+": 28 | return left + right 29 | elif root.operation == "-": 30 | return left - right 31 | elif root.operation == "/": 32 | return left / right 33 | 34 | def construct_tree(): 35 | root = Node(operation='+') 36 | root.left = Node(operation='*') 37 | root.right = Node(value=5) 38 | root.left.left = Node(value=25) 39 | root.left.right = Node(value=6) 40 | 41 | return root 42 | 43 | if __name__ == "__main__": 44 | root = construct_tree() 45 | print parse_tree(root) 46 | -------------------------------------------------------------------------------- /data_structures/binary_search_tree.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | ''' 4 | Program to implement binary search tree 5 | 6 | ''' 7 | 8 | class Node(object): 9 | ''' 10 | Node class represents each node of the binary search tree 11 | ''' 12 | def __init__(self, data): 13 | self.data = data 14 | self.left = None 15 | self.right = None 16 | 17 | 18 | class BinarySearchTree(object): 19 | ''' 20 | Class to represent binary search tree 21 | ''' 22 | def __init__(self): 23 | self.root = None 24 | 25 | def insert(self, data, root): 26 | ''' 27 | Function to insert node in a binary search tree 28 | ''' 29 | if root is None: 30 | return Node(data) 31 | 32 | elif data <= root.data: 33 | root.left = self.insert(data, root.left) 34 | 35 | else: 36 | root.right = self.insert(data, root.right) 37 | 38 | return root 39 | 40 | def inorder(self, node): 41 | ''' 42 | Function to print the inorder traversal of the tree 43 | ''' 44 | if node is None: 45 | return 46 | 47 | self.inorder(node.left) 48 | print (node.data) 49 | self.inorder(node.right) 50 | 51 | 52 | def initialize(): 53 | ''' 54 | Function to initialize a binary search tree 55 | ''' 56 | bst = BinarySearchTree() 57 | 58 | bst.root = bst.insert(6, bst.root) 59 | bst.insert(1, bst.root) 60 | bst.insert(9, bst.root) 61 | bst.insert(3, bst.root) 62 | bst.inorder(bst.root) 63 | 64 | return bst 65 | 66 | if __name__ == "__main__": 67 | initialize() 68 | -------------------------------------------------------------------------------- /data_structures/binary_tree.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | ''' 4 | Class to represent a binary tree node 5 | ''' 6 | 7 | class BinaryTreeNode(object): 8 | 9 | def __init__(self, data): 10 | self.data = data 11 | self.left = None 12 | self.right = None 13 | 14 | def add_left(self, left): 15 | ''' 16 | Function to add node at the left of the current node 17 | ''' 18 | self.left = left 19 | 20 | def add_right(self, right): 21 | ''' 22 | Function to add node at the right of the current node 23 | ''' 24 | self.right = right 25 | 26 | def __str__(self): 27 | text = str(self.data) 28 | text += ":" 29 | 30 | child = {} 31 | if self.left: 32 | child["left"] = str(self.left) 33 | 34 | if self.right: 35 | child["right"] = str(self.right) 36 | 37 | text += str(child) 38 | return text 39 | 40 | 41 | def initialize(): 42 | root = BinaryTreeNode(5) 43 | 44 | # Initialize the child nodes 45 | child_1 = BinaryTreeNode(4) 46 | child_2 = BinaryTreeNode(6) 47 | child_child_1 = BinaryTreeNode(3) 48 | child_child_2 = BinaryTreeNode(1) 49 | child_child_3 = BinaryTreeNode(13) 50 | child_child_4 = BinaryTreeNode(12) 51 | 52 | # Connect the child nodes to the parent node 53 | child_1.add_left(child_child_1) 54 | child_1.add_right(child_child_2) 55 | child_2.add_left(child_child_3) 56 | child_2.add_right(child_child_4) 57 | root.add_left(child_1) 58 | root.add_right(child_2) 59 | 60 | print root 61 | return root 62 | 63 | if __name__ == "__main__": 64 | initialize() 65 | -------------------------------------------------------------------------------- /data_structures/queue.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | ''' 4 | Class to implement queue in Python 5 | 6 | Queue: A queue stores elements in a linear fashion such that they follow first-in-first-out(FIFO) order. The elements 7 | are added to the top of the queue and removed from the beginning of the queue. 8 | 9 | ''' 10 | 11 | class Node(object): 12 | 13 | def __init__(self, data, nextnode=None): 14 | self.data = data 15 | self.nextnode = nextnode 16 | 17 | 18 | class Queue(object): 19 | 20 | def __init__(self): 21 | self.first = None 22 | self.last = None 23 | 24 | def enqueue(self, data): 25 | ''' 26 | Function to insert elements into a queue 27 | ''' 28 | node = Node(data) 29 | 30 | if self.first is None: 31 | self.first = node 32 | self.last = node 33 | 34 | else: 35 | self.last.nextnode = node 36 | self.last = self.last.nextnode 37 | 38 | 39 | def deque(self): 40 | ''' 41 | Function to delete elements from a queue 42 | ''' 43 | 44 | if self.first is None: 45 | raise ValueError("Queue is empty") 46 | 47 | print self.first.data, "is removed" 48 | 49 | self.first = self.first.nextnode 50 | 51 | def isEmpty(self): 52 | ''' 53 | Function to check if the queue is empty 54 | ''' 55 | if self.first is None: 56 | return True 57 | 58 | return False 59 | 60 | def peek(self): 61 | ''' 62 | Function to print the top of the queue 63 | ''' 64 | if self.last is None: 65 | raise ValueError("No element is there in queue") 66 | 67 | return self.last.data 68 | -------------------------------------------------------------------------------- /data_structures/stack.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | ''' 4 | Problem: Implement stack in python 5 | 6 | Stack: A stack is a data structure which stores elements in a linear fashion such that each element is added and 7 | removed from the top of the stack. 8 | 9 | ''' 10 | 11 | class Node(object): 12 | 13 | def __init__(self, data, nextnode=None): 14 | self.data = data 15 | self.nextnode = nextnode 16 | 17 | class Stack(object): 18 | 19 | def __init__(self): 20 | self.top = None 21 | 22 | def is_empty(self): 23 | ''' 24 | Function to check if the stack is empty 25 | ''' 26 | if self.top is None: 27 | return True 28 | 29 | return False 30 | 31 | def push(self, data): 32 | ''' 33 | Function to insert an element to the top of a stack 34 | ''' 35 | node = Node(data=data) 36 | 37 | if self.top is None: 38 | self.top = node 39 | 40 | else: 41 | temp = self.top 42 | self.top = node 43 | self.top.nextnode = temp 44 | 45 | def pop(self): 46 | ''' 47 | Function to delete an element from the top of the stack 48 | ''' 49 | if self.top is None: 50 | raise ValueError("Stack is empty") 51 | 52 | else: 53 | print self.top.data, "is deleted" 54 | self.top = self.top.nextnode 55 | 56 | 57 | def peek(self): 58 | ''' 59 | Function to get the top of the stack 60 | ''' 61 | if self.top is None: 62 | raise ValueError("Stack is empty") 63 | 64 | else: 65 | return (self.top.data) 66 | -------------------------------------------------------------------------------- /random_problems/LRUCache.py: -------------------------------------------------------------------------------- 1 | class LRUCache: 2 | 3 | # @param capacity, an integer 4 | def __init__(self, capacity): 5 | self.hash_map = {} 6 | self._list = [] 7 | self.node_map = {} 8 | self.capacity = capacity 9 | self.n = 0 10 | 11 | # @return an integer 12 | def get(self, key): 13 | if not self.hash_map.get(key, None): 14 | return -1 15 | 16 | else: 17 | index = self.node_map.get(key, None) 18 | self._list.pop(index) 19 | self._list.append(key) 20 | self.node_map[key] = self.n-1 21 | return self.hash_map[key] 22 | 23 | 24 | # @param key, an integer 25 | # @param value, an integer 26 | # @return nothing 27 | def set(self, key, value): 28 | if self.n == self.capacity: 29 | last_val = self._list[0] 30 | self._list.pop(0) 31 | self.node_map.pop(last_val, None) 32 | self.hash_map.pop(last_val, None) 33 | self.n -= 1 34 | 35 | 36 | self.hash_map[key] = value 37 | self._list.append(key) 38 | self.n += 1 39 | self.node_map[key] = self.n - 1 40 | 41 | 42 | -------------------------------------------------------------------------------- /random_problems/flatten_dict.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | ''' 3 | A dictionary is a type of data structure that is supported natively in all major interpreted languages 4 | such as JavaScript, Python, Ruby and PHP, where it’s known as an Object, Dictionary, Hash and Array, 5 | respectively. In simple terms, a dictionary is a collection of unique keys and their values. The values can 6 | typically be of any primitive type (i.e an integer, boolean, double, string etc) or other dictionaries 7 | (dictionaries can be nested). 8 | 9 | Given a dictionary dict, write a function flattenDictionary that returns a flattened version of it. 10 | 11 | input: dict = { 12 | Key1 : 1, 13 | Key2 : { 14 | a : 2, 15 | b : 3, 16 | c : { 17 | d : 3, 18 | e : 1 19 | } 20 | } 21 | } 22 | 23 | output: { 24 | Key1: 1, 25 | Key2.a: 2, 26 | Key2.b : 3, 27 | Key2.c.d : 3, 28 | Key2.c.e : 1 29 | } 30 | 31 | ''' 32 | 33 | # Time-complexity: O(n) where n will be numder of keys in the input dictionary 34 | # if you represent all the keys in a tree 35 | # O(N) since the output dictionary is asymptotically as big as the input dictionary. 36 | def flattendict(s, input, output): 37 | for key, value in input.iteritems(): 38 | new_key = s + key 39 | if type(value) == type({}): 40 | flattendict(new_key + ".", value, output) 41 | else: 42 | output[new_key] = value 43 | 44 | if __name__ == "__main__": 45 | input = { 46 | "Key1" : 1, 47 | "Key2" : { 48 | "a" : 2, 49 | "b" : 3, 50 | "c" : { 51 | "d" : 3, 52 | "e" : 1 53 | } 54 | }, 55 | "Key3": 4 56 | } 57 | 58 | output = {} 59 | flattendict("", input, output) 60 | print output 61 | -------------------------------------------------------------------------------- /random_problems/google.py: -------------------------------------------------------------------------------- 1 | Question 2 | 3 | Implement a mobile keyboard autocorrect system. 4 | For an input word, get the 3 closest words from a dictionary 5 | Assume english dictionary 6 | Simplify: limit to words with distance 1 7 | Simplify: only consider substitution (i.e. not missing letters) 8 | Simplify: ignore case (i.e. assume all lowercase) 9 | 10 | Examples: 11 | 12 | Dictionary: Cat, Car, Pat, Par, Rate, Kite, Mate, Rite 13 | Input: Cat 14 | Output: Cat, Car, Pat, Cbt 15 | 16 | Input: Kate 17 | Output: Rate, Kite, Mate, Pate 18 | 19 | 20 | Please use this Google doc to code during your interview. To free your hands for coding, we recommend that you use a headset or a phone with speaker option. 21 | 22 | no of suggestions = 3 23 | Hi 24 | 25 | A-Z except C 26 | 27 | Cat 28 | 29 | O(25*length(word)) 30 | 31 | 25 (list of words) 32 | 33 | comparison (n) 34 | Aat - 10 35 | Bat - 3 36 | , Cat, Pat, Kat, 37 | 38 | Aat, Bat, 39 | 40 | n words for length(word) 41 | (k*2) k is length(word) 42 | 43 | Cat Pat 44 | distance = 1 45 | n * (k) is length(word) 46 | 47 | {‘a’: [{1: ‘a’, 2: ‘ab’} 48 | 49 | dict[‘a’][1] 50 | 51 | {‘a’: [‘a’: [], ‘b’: [] 52 | 53 | def get_dictionary_suggestions(word, dictionary): 54 | # all words with length(word) 55 | all_words = [] 56 | length = len(word) 57 | start = ord(‘A’) 58 | for i in range(start, start+26): 59 | # all words starting from chr(i) and length 60 | words = get_from_dictionary(dictionary, length, chr(i)) 61 | all_words += words 62 | 63 | suggestions = [] 64 | if get_valid_word(dictionary, word): 65 | suggestions.append(word) 66 | 67 | for w in all_words: 68 | distance = 0 69 | for i in range(length): 70 | if w[i] != word[i]: 71 | distance += 1 72 | if distance == 1: 73 | suggestions.append(w) 74 | if len(suggestions) == 3: 75 | return suggestions 76 | 77 | return suggestions 78 | 79 | 80 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /random_problems/isValidSudoku.py: -------------------------------------------------------------------------------- 1 | def isValidSudoku(A): 2 | n = len(A) 3 | 4 | for i in range(n): 5 | hash_h = {} 6 | hash_v = {} 7 | for j in range(n): 8 | # print A[i][j] 9 | if hash_h.get(A[i][j], None) or hash_v.get(A[j][i], None): 10 | return 0 11 | # print "ascca" 12 | if A[i][j] != ".": 13 | print "vvs", A[i][j] 14 | hash_h[A[i][j]] = 1 15 | if A[j][i] != ".": 16 | hash_v[A[j][i]] = 1 17 | # print hash_v 18 | # print hash_h 19 | 20 | for i in range(0, n-2, 3): 21 | hash_v = {} 22 | for j in range(0, n-2, 3): 23 | if hash_v.get(A[i][i+j], None): 24 | return 0 25 | 26 | elif A[i][i+j] != '.': 27 | hash_v[A[i][i+j]] = 1 28 | 29 | return 1 30 | 31 | A = [ "....5..1.", ".4.3.....", ".....3..1", "8......2.", "..2.7....", ".15......", ".....2...", ".2.9.....", "..4......" ] 32 | print A 33 | print isValidSudoku(A) 34 | -------------------------------------------------------------------------------- /random_problems/n_chocolates.py: -------------------------------------------------------------------------------- 1 | import heapq 2 | def nchoc(A, B): 3 | heap = [] 4 | for h in B: 5 | heapq.heappush(heap, -h) 6 | 7 | i = 0 8 | count = 0 9 | k = 0 10 | while (i < A): 11 | if heap: 12 | k = heapq.heappop(heap) 13 | print k 14 | count += k 15 | if k/2 < 0: 16 | heapq.heappush(heap, k/2) 17 | i += 1 18 | 19 | return -count 20 | 21 | 22 | A = 10 23 | B = [ 2147483647, 2000000014, 2147483647 ] 24 | print nchoc(A, B) -------------------------------------------------------------------------------- /random_problems/points_on_a_straight_line.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | ''' 4 | Given n points on a 2D plane, find the maximum number of points that lie on the same straight line. 5 | 6 | Sample Input: (1, 1) (2, 2) 7 | 8 | Sample Output: 2 9 | ''' 10 | 11 | def points_on_line(points_x, points_y): 12 | if len(points_x) != len(points_y): 13 | return -1 14 | 15 | n = len(points_x) 16 | if n <= 2: 17 | return n 18 | 19 | max_points = 0 20 | 21 | for i in range(n): 22 | dup = 1 # count the duplicates 23 | slopes = {} 24 | 25 | for j in range(n): 26 | if points_x[i] == points_x[j] and points_y[i] == points_y[j] and i != j: 27 | dup += 1 28 | elif i != j: 29 | if points_x[i] == points_x[j]: 30 | slopes['v'] = slopes.get('v', 0) + 1 31 | elif points_y[i] == points_y[j]: 32 | slopes['h'] = slopes.get('h', 0) + 1 33 | 34 | else: 35 | slope = (points_y[i] - points_y[j]) / float(points_x[i] - points_x[j]) 36 | slopes[slope] = slopes.get(slope, 0) + 1 37 | 38 | if len(slopes) > 0: 39 | max_points = max(max_points, max(slopes.values())+dup) 40 | else: 41 | max_points = max(max_points, dup) 42 | 43 | return max_points 44 | 45 | print points_on_line([0, 1, -1], [0, 1, -1]) 46 | -------------------------------------------------------------------------------- /random_problems/smallest_permutation_larger_than_original_number.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Given a number, find the number which is permutation of the given number and is just greater 3 | than the current number. 4 | 5 | Example: 6 | 15432 -> 21345 7 | 546531 -> 551346 8 | 9 | 10 | Step1: Identify the decending sequence in the end, if not present return None 11 | 98393282914[6531] 12 | 13 | Step2: Reverse the seq if found 14 | 98393282914[1356] 15 | 16 | Step3: Swap the pivot number with a number just larger than the pivot in the seq 17 | 4 <-> 5 18 | ''' 19 | 20 | def smallestPermutationLargerThanOriginalNumber(num): 21 | s = list(str(num)) 22 | 23 | for i in range(len(s)-1, 0, -1): 24 | if int(s[i]) > int(s[i-1]): 25 | break 26 | 27 | pivot = i-1 28 | if pivot >= 0: 29 | s = s[:i] + s[:i-1:-1] 30 | 31 | current = pivot+1 32 | while (int(s[current]) < int(s[pivot])): 33 | current += 1 34 | 35 | s[current], s[pivot] = s[pivot], s[current] 36 | 37 | return int(''.join(s)) 38 | else: 39 | return None 40 | 41 | print smallestPermutationLargerThanOriginalNumber(15432) 42 | print smallestPermutationLargerThanOriginalNumber(546531) 43 | print smallestPermutationLargerThanOriginalNumber(983932829146531) 44 | -------------------------------------------------------------------------------- /random_problems/smallest_sequence_with_given_primes.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Given three prime number(p1, p2, p3) and an integer k. Find the first(smallest) k integers which have only 3 | p1, p2, p3 or a combination of them as their prime factors. 4 | 5 | Example: 6 | 7 | Input : 8 | Prime numbers : [2,3,5] 9 | k : 5 10 | 11 | If primes are given as p1=2, p2=3 and p3=5 and k is given as 5, then the sequence of first 5 integers will be: 12 | 13 | Output: 14 | {2,3,4,5,6} 15 | 16 | Explanation : 17 | 4 = p1 * p1 ( 2 * 2 ) 18 | 6 = p1 * p2 ( 2 * 3 ) 19 | ''' 20 | 21 | import heapq 22 | class Solution: 23 | # @param A : integer 24 | # @param B : integer 25 | # @param C : integer 26 | # @param D : integer 27 | # @return a list of integers 28 | def solve(self, A, B, C, D): 29 | prime_numbers = [A, B, C] 30 | queue = [] 31 | visited = {} 32 | 33 | for number in prime_numbers: 34 | if not visited.get(number, None): 35 | heapq.heappush(queue, number) 36 | visited[number] = 1 37 | 38 | index = 0 39 | solution = [] 40 | 41 | while index < D and len(queue): 42 | current = heapq.heappop(queue) 43 | solution.append(current) 44 | 45 | for number in prime_numbers: 46 | new_number = current*number 47 | 48 | if not visited.get(new_number, None): 49 | heapq.heappush(queue, new_number) 50 | visited[new_number] = 1 51 | index += 1 52 | 53 | return solution 54 | 55 | s = Solution() 56 | print s.solve(3, 11, 7, 50) 57 | -------------------------------------------------------------------------------- /searching/jump_search: -------------------------------------------------------------------------------- 1 | import math 2 | 3 | def jumpSearch( arr , x , n ): 4 | step = math.sqrt(n) 5 | prev = 0 6 | while arr[int(min(step, n)-1)] < x: 7 | prev = step 8 | step += math.sqrt(n) 9 | if prev >= n: 10 | return -1 11 | while arr[int(prev)] < x: 12 | prev += 1 13 | 14 | if prev == min(step, n): 15 | return -1 #element is not found 16 | if arr[int(prev)] == x: 17 | return prev 18 | 19 | return -1 20 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/charulagrl/data-structures-and-algorithms/79ccc0aa34d9e3eb508bfe64d1fcee9dd1ea5e98/tests/__init__.py -------------------------------------------------------------------------------- /tests/searching_test.py: -------------------------------------------------------------------------------- 1 | # Writing unit-test for the searching algorithms 2 | import unittest 3 | 4 | '''Test cases for searching functions''' 5 | from searching import binary_search, ternary_search 6 | 7 | import unittest 8 | 9 | class MyTest(unittest.TestCase): 10 | def setUp(self): 11 | self.array = [2, 5, 6, 12, 17, 23, 34, 45, 67, 81] 12 | self.high = len(self.array) 13 | self.key = 23 14 | 15 | def test_binary_search(self): 16 | self.assertEqual(binary_search.binary_search(0, self.high, self.key, self.array), 5) 17 | 18 | def test_ternary_search(self): 19 | self.assertEqual(ternary_search.ternary_search(0, self.high, self.key, self.array), 5) 20 | -------------------------------------------------------------------------------- /tests/sorting_test.py: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | '''Test cases for sorting functions''' 4 | 5 | import unittest 6 | 7 | from sorting import bubble_sort, selection_sort, insertion_sort, merge_sort 8 | 9 | class MyTest(unittest.TestCase): 10 | def setUp(self): 11 | self.array = [34, 45, 2, 6, 64, 3, 17, 12, 23, 67, 81, 8] 12 | self.sorted_array = [2, 3, 6, 8, 12, 17, 23, 34, 45, 64, 67, 81] 13 | 14 | def test_bubble_sort(self): 15 | self.assertEqual(bubble_sort.bubble_sort(self.array), self.sorted_array) 16 | 17 | def test_insertion_sort(self): 18 | self.assertEqual(insertion_sort.insertion_sort(self.array), self.sorted_array) 19 | 20 | def test_merge_sort(self): 21 | self.assertEqual(merge_sort.merge_sort(self.array), self.sorted_array) 22 | 23 | def test_selection_sort(self): 24 | self.assertEqual(selection_sort.selection_sort(self.array), self.sorted_array) 25 | --------------------------------------------------------------------------------