├── README.md ├── W1_L1_gcd1.py ├── W1_L2_gcd2.py ├── W1_L2_gcd_euclid.png ├── W1_L2_gcd_euclid.py ├── W1_L3_gcd_best.png ├── W1_L3_gcd_best.py ├── W1_quiz.py ├── W2_L1_basics.py ├── W2_L2_strings.py ├── W2_L3_lists.py ├── W2_L4_controlflow.py ├── W2_L5_functions.py ├── W2_L6_examples.py ├── W2_programming_assignment.py ├── W2_quiz.py ├── W3_L1_range.py ├── W3_L2_manipulating_lists.py ├── W3_L3_break_out_of_loop.py ├── W3_L4_Array.png ├── W3_L4_Lists.png ├── W3_L4_Operations.png ├── W3_L4_arrays_lists_binarysearch.py ├── W3_L5_efficiency.py ├── W3_L6_selection_sort.py ├── W3_L7_insertion_bubble_sort.py ├── W3_L8_recursion.py ├── W3_programming_assignment.py ├── W4_L1_merge_sort.py ├── W4_L3_quick_sort.py ├── W4_L5_tuples_dictionaries.py ├── W4_L6_function_definitions.py ├── W4_L7_list_comprehension.py ├── W4_programming_assignment.py ├── W4_quiz.py ├── W5_L1_exception_handling.py ├── W5_L2_standard_input_output.py ├── W5_L3_file1.txt ├── W5_L3_handling_files.py ├── W5_L4_string_functions.py ├── W5_L5_formatting_printed_output.py ├── W5_L6_pass_del_None.py ├── W5_programming_assignment.py ├── W6_L1_PythonCode.py ├── W6_L2_globalscope_nestedfunctions.py ├── W6_L3_generating_permutations.py ├── W6_L4_sets_stacks_queues.py ├── W6_L5_priority_queues_heaps.py ├── W6_quiz.py ├── W7_L1_abstractDatatypes_classes_objects.py ├── W7_L2_classes_objects_in_Python.py ├── W7_L3_PythonCode.py ├── W7_L3_user_defined_lists.py ├── W7_L4_PythonCode.py ├── W7_L4_binary_search_trees.py ├── W7_quiz.py ├── W8_L1_memoization_dynamic_programming.py ├── W8_L4_matrix_multiplication.py ├── W8_L5_Wrap_up.py └── W8_programming_assignment.py /README.md: -------------------------------------------------------------------------------- 1 |

The learning path on DSA using Python in my NPTEL course

2 | -------------------------------------------------------------------------------- /W1_L1_gcd1.py: -------------------------------------------------------------------------------- 1 | def gcd(m, n): 2 | m_lst = [] 3 | for i in range(1, m+1): 4 | if m % i == 0: 5 | m_lst.append(i) 6 | 7 | n_lst = [] 8 | for i in range(1, n+1): 9 | if n % i == 0: 10 | n_lst.append(i) 11 | 12 | common_lst = [] 13 | for i in m_lst: 14 | if i in n_lst: 15 | common_lst.append(i) 16 | 17 | return common_lst[-1] 18 | 19 | print(gcd(15, 20)) -------------------------------------------------------------------------------- /W1_L2_gcd2.py: -------------------------------------------------------------------------------- 1 | def gcd(m, n): 2 | for i in range(min(m, n), 0, -1): 3 | if m % i == 0 and n % i == 0: 4 | return i 5 | 6 | 7 | 8 | print(gcd(150, 75)) 9 | -------------------------------------------------------------------------------- /W1_L2_gcd_euclid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joswinemmanuel/NPTEL-DSA-Python/69a467d94f32c49c1206c19f40d1842e7b5e7235/W1_L2_gcd_euclid.png -------------------------------------------------------------------------------- /W1_L2_gcd_euclid.py: -------------------------------------------------------------------------------- 1 | def gcd(m, n): 2 | if m < n: 3 | m, n = n, m 4 | 5 | if m % n == 0: 6 | return n 7 | else: 8 | diff = m - n 9 | return gcd(max(n, diff), min(n, diff)) 10 | 11 | print(gcd(75, 30)) -------------------------------------------------------------------------------- /W1_L3_gcd_best.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joswinemmanuel/NPTEL-DSA-Python/69a467d94f32c49c1206c19f40d1842e7b5e7235/W1_L3_gcd_best.png -------------------------------------------------------------------------------- /W1_L3_gcd_best.py: -------------------------------------------------------------------------------- 1 | def gcd(m, n): 2 | 3 | if n == 0: 4 | return m 5 | return gcd(n, m % n) 6 | 7 | # if m < n: 8 | # m, n = n, m 9 | 10 | # if m % n == 0: 11 | # return n 12 | # else: 13 | # return gcd(n, m % n) # m % n < n always 14 | 15 | print(gcd(30, 75)) -------------------------------------------------------------------------------- /W1_quiz.py: -------------------------------------------------------------------------------- 1 | # def h(x): 2 | # (d,n) = (1,0) 3 | # while d <= x: 4 | # (d,n) = (d*3,n+1) 5 | # return(n) 6 | 7 | # print(h(27993)) 8 | 9 | 10 | # def g(n): 11 | # s=0 12 | # for i in range(2,n): 13 | # if n%i == 0: 14 | # s = s+1 15 | # return(s) 16 | 17 | # print(g(60) - g(48)) 18 | 19 | 20 | # def f(n): 21 | # s=0 22 | # for i in range(1,n+1): 23 | # if n//i == i and n%i == 0: 24 | # s = 1 25 | # return(s%2 == 1) 26 | 27 | # for i in range(1, 50): 28 | # print(i, f(i)) 29 | 30 | 31 | 32 | # def foo(m): 33 | # if m == 0: 34 | # return(0) 35 | # else: 36 | # return(m+foo(m-1)) 37 | 38 | # for i in range(0, 6): 39 | # print(i, foo(i), int(i*(i+1)/2)) -------------------------------------------------------------------------------- /W2_L1_basics.py: -------------------------------------------------------------------------------- 1 | """ float the sentence break up into MANTISSA and EXPONENT """ 2 | # 1.2345 = 12345 x 10**-4 3 | # (mantissa) (exponent) 4 | # 0.602 X 10**24 5 | #(mantissa) (exponent) 6 | 7 | def divides(m, n): 8 | return True if m % n == 0 else False 9 | 10 | def even(m): 11 | return divides(m, 2) 12 | 13 | def odd(m): 14 | return not even(m) 15 | 16 | print(divides(10, 5)) 17 | print(even(8)) 18 | print(odd(8)) 19 | print(odd(5)) 20 | -------------------------------------------------------------------------------- /W2_L2_strings.py: -------------------------------------------------------------------------------- 1 | city = 'Kerala\'s' # single quotes can be used 2 | title = "\"Joswin the great\"" # double quotes can be used 3 | paragraph = '''What's "going on" 4 | Why's "this" different''' # triple quotes can be used 5 | print(city) 6 | print(title) 7 | print(paragraph) 8 | 9 | s1 = "Joswin" 10 | s2 = " Emmanuel" 11 | 12 | print(s1[1:5]) # string slicing 13 | 14 | print(s1[::-1]) # string reversing 15 | 16 | print(s1 + s2) # concatenation 17 | 18 | print(len(s1)) # getting length of the string -------------------------------------------------------------------------------- /W2_L3_lists.py: -------------------------------------------------------------------------------- 1 | names = ["joswin", "agath", "merin"] 2 | age = [19, 23, 53] 3 | mixed = ["joswin", 19, "agath", 23] 4 | 5 | lst = [1, 2, 3, 4, 5] 6 | 7 | print(lst[1:3]) # list slicing 8 | 9 | print(lst[::-1]) # returns the reverse of the list using slicing 10 | 11 | print(len(lst)) # returns the lenght of the list 12 | 13 | ''' lst[0] not equal to lst[0:1] ''' 14 | ''' lst[0] gives 1 lst[0:1] gives [1] ''' 15 | 16 | lst[0] = 10 # lists are mutable 17 | print(lst[0]) 18 | 19 | a = 10 20 | b = a 21 | a = 99 22 | print("a = ", a, "b = ", b) 23 | """ Value of b will still be 10 -- this is the case for immutalble datatypes only 24 | like int, float, str etc -- a fresh copy is made while assigining""" 25 | a = [1, 2, 3] 26 | b = a # shallow copy 27 | a[1] = 100 28 | b[2] = 99 29 | print("a = ", a, "b = ", b) 30 | """ for mutable datatypes like list when value in list is changed it change for both""" 31 | """ so we make a copy in b using .copy() funciton""" 32 | a = [1, 2, 3] 33 | b = a.copy() # deep copy 34 | c = a[:] # or use [::] slicing to make a deep copy 35 | a[1] = 100 36 | b[2] = 99 37 | print("a = ", a, "b = ", b, "c = ", c) 38 | 39 | l1 = [1, 2, 3] 40 | l2 = [1, 2, 3] 41 | l3 = l2 42 | print(l1 == l2) # == checks if l1 and l2 have same value 43 | print(l2 == l3) 44 | print(l1 is l2) # is checks if l1 and l2 refers to same object 45 | print(l2 is l3) 46 | 47 | print(l1 + l2) # concatenation of lists using -------------------------------------------------------------------------------- /W2_L4_controlflow.py: -------------------------------------------------------------------------------- 1 | ''' 0 (zero), "", '' (empty string), [], {}, () are treated as False ''' 2 | 3 | """ conditional execution 4 | repeated execution 5 | function execution """ 6 | # all basic knowledge on control flow 7 | 8 | # to print all the factors of an inputted number 9 | 10 | number = int(input("Enter the number : ")) 11 | for i in range(1, number+1): 12 | if number % i == 0: 13 | print(i, end=" ") -------------------------------------------------------------------------------- /W2_L5_functions.py: -------------------------------------------------------------------------------- 1 | """ A function calling itself is RECUSION """ 2 | 3 | def factorial(n): 4 | if n <= 1: 5 | return 1 6 | return n * factorial(n-1) 7 | 8 | print(factorial(5)) 9 | 10 | def fibonacci_n(n): 11 | if n<=1: 12 | return n 13 | return fibonacci_n(n-1) + fibonacci_n(n-2) 14 | 15 | for i in range(0, 10): 16 | print(fibonacci_n(i), end=" ") -------------------------------------------------------------------------------- /W2_L6_examples.py: -------------------------------------------------------------------------------- 1 | def is_prime(n): 2 | if n <= 1: 3 | return False 4 | for i in range(2, int(n**(1/2))+1): 5 | if n % i == 0: 6 | return False 7 | return True 8 | 9 | def primes_upto(n): 10 | for i in range(0, n): 11 | if is_prime(i): 12 | print(i, end=" ") 13 | print() 14 | 15 | def n_primes(n): 16 | i = 1 17 | while n != 0: 18 | i += 1 19 | if is_prime(i): 20 | print(i, end=" ") 21 | n -= 1 22 | 23 | primes_upto(10) 24 | 25 | n_primes(9) -------------------------------------------------------------------------------- /W2_programming_assignment.py: -------------------------------------------------------------------------------- 1 | """ 1) Write a function intreverse(n) that takes as input a positive integer n 2 | and returns the integer obtained by reversing the digits in n. 3 | Here are some examples of how your function should work. """ 4 | 5 | def intreverse(n): 6 | return int(str(n)[::-1]) 7 | 8 | """ 2) Write a function matched(s) that takes as input a string s and checks if the brackets 9 | "(" and ")" in s are matched: that is, every "(" has a matching ")" after it and every ")" 10 | has a matching "(" before it. Your function should ignore all other symbols that appear in s. 11 | Your function should return True if s has matched brackets and False if it does not. 12 | Here are some examples to show how your function should work """ 13 | 14 | def matched(s): 15 | stack = [] 16 | for i in s: 17 | if i == "(": 18 | stack.append(i) 19 | elif i == ")": 20 | if len(stack) > 0: 21 | stack.pop() 22 | else: 23 | return False 24 | if len(stack) == 0: 25 | return True 26 | else: 27 | return False 28 | 29 | """ 3) Write a function sumprimes(l) that takes as input a list of integers l 30 | and retuns the sum of all the prime numbers in l. 31 | Here are some examples to show how your function should work. """ 32 | 33 | def sumprimes(l): 34 | def if_prime(n): 35 | if n <= 1: 36 | return False 37 | for i in range(2, int(n**(1/2))+1): 38 | if n % i == 0: 39 | return False 40 | return True 41 | sum = 0 42 | for i in l: 43 | if if_prime(i): 44 | sum += i 45 | return sum 46 | -------------------------------------------------------------------------------- /W2_quiz.py: -------------------------------------------------------------------------------- 1 | # x = [[3,5],"mimsy",2,"borogove",1] # Statement 1 2 | # y = x[0:50] # Statement 2 3 | # z = y # Statement 3 4 | # w = x # Statement 4 5 | # x[1] = x[1][:5] + 'ery' # Statement 5 6 | # y[1] = 4 # Statement 6 7 | # w[1][:3] = 'fea' # Statement 7 8 | # z[4] = 42 # Statement 8 9 | # x[0][0] = 5555 # Statement 9 10 | # a = (x[3][1] == 1) 11 | 12 | # #Error statement 7 13 | 14 | # b = [43,99,65,105,4] 15 | # a = b[2:] 16 | # d = b[1:] 17 | # c = b 18 | # d[1] = 95 19 | # b[2] = 47 20 | # c[3] = 73 21 | # print(b) 22 | # print(a) 23 | # print(d) 24 | # print(c) 25 | # print(f"a[0] == {a[0]}, b[3] == {b[3]}, c[3] == {c[3]}, d[1] == {d[1]}") 26 | 27 | # startmsg = "anaconda" 28 | # endmsg = "" 29 | # for i in range(1,1+len(startmsg)): 30 | # endmsg = endmsg + startmsg[-i] 31 | # print(endmsg) 32 | 33 | # def mystery(l): 34 | # l = l[2:] 35 | # return(l) 36 | 37 | # mylist = [7,11,13,17,19,21] 38 | # mystery(mylist) 39 | # print(mylist) -------------------------------------------------------------------------------- /W3_L1_range.py: -------------------------------------------------------------------------------- 1 | print(list(range(1, 11))) 2 | # gives [1 ... 10] ( till n-1 ) 3 | 4 | print(list(range(1, 11, 2))) 5 | # the third argument is the jump value 6 | 7 | print(list(range(10, 0, -1))) 8 | # count down 9 | 10 | print(range(1, 4) == [1, 2, 3]) 11 | # False because range(1, 4) gives a range object while [1, 2, 3] is a list object 12 | print(type(range(1, 4))) 13 | print(type([1, 2, 3])) 14 | 15 | # Type conversion 16 | print(int('10') + 1) # 11 17 | print(str(100) + '1') # 1001 -------------------------------------------------------------------------------- /W3_L2_manipulating_lists.py: -------------------------------------------------------------------------------- 1 | list1 = [1, 2, 3, 4, 5] 2 | list1[1] = 1000 3 | # [1, 1000, 3, 4, 5] 4 | print(list1) 5 | 6 | list2 = list1 # list2 will be list1, shallow copy, any change to list1 changes list2 7 | 8 | list1 = list1[:2] + [999] + list1[3:] # list1 is changed to a new list so list2 remains same as old list1 9 | # list1 = [1, 1000, 999, 4, 5] 10 | print(list1) 11 | # list2 = [1, 1000, 3, 4, 5] 12 | print(list2) 13 | 14 | list1 = [1, 2, 3] 15 | list1 = list2 # shallow copy 16 | list1.append(100) # list1 is manipulated, added a new value, 17 | # not changed into a new list, so list2 also changed 18 | print(list1) 19 | print(list2) 20 | # both will be [1, 2, 3, 100] 21 | 22 | " Shrink and Expand list using slices " 23 | list1 = [1, 2, 3, 4, 5] 24 | list1[2:] = [33, 44, 55, 66] 25 | print(list1) # [1, 2, 33, 44, 55, 66] Expanded using slices 26 | list1[0:2] = [1000] 27 | print(list1) # [1000, 33, 44, 55, 66] Shrinked using slices 28 | 29 | " 'in' is called membership operator " 30 | # used to check if an element is in a list or string 31 | print('IN') if 1 in [1, 2, 3] else print('NOT IN') # IN 32 | print('IN') if 100 in [1, 2, 3] else print('NOT IN') # NOT IN 33 | 34 | " removing all occurance of an element " 35 | lst = [1, 2, 3, 1, 4, 1, 5] 36 | while 1 in lst: 37 | lst.remove(1) 38 | print(lst) # [2, 3, 4, 5] 39 | -------------------------------------------------------------------------------- /W3_L3_break_out_of_loop.py: -------------------------------------------------------------------------------- 1 | def find_pos(lst, item): 2 | for i in range(len(lst)): 3 | if lst[i] == item: 4 | return i 5 | return -1 6 | " gives the index of item in list, if item not in list gives -1 " 7 | 8 | # break can be used to break out of a loop 9 | for i in range(1, 10): 10 | if i == 6: 11 | break 12 | print(i, end=" ") 13 | # 1 2 3 4 5 when i == 6 it breaks out of the loop 14 | print() 15 | 16 | " In Python there is an 'else' case for the 'for', it executes after all the iteration in the loop " 17 | for i in range(1, 6): 18 | print(i, end=' ') 19 | else: 20 | print("all values printed") 21 | # 1 2 3 4 5 all values printed 22 | 23 | " but if the loop is breaked out using 'break' 'else' case won't be executed " 24 | for i in range(1, 6): 25 | if i == 3: 26 | break 27 | print(i, end=' ') 28 | else: 29 | print("all values printed") 30 | # 1 2 see else case didn't get exectued as loop was breaked 31 | -------------------------------------------------------------------------------- /W3_L4_Array.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joswinemmanuel/NPTEL-DSA-Python/69a467d94f32c49c1206c19f40d1842e7b5e7235/W3_L4_Array.png -------------------------------------------------------------------------------- /W3_L4_Lists.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joswinemmanuel/NPTEL-DSA-Python/69a467d94f32c49c1206c19f40d1842e7b5e7235/W3_L4_Lists.png -------------------------------------------------------------------------------- /W3_L4_Operations.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joswinemmanuel/NPTEL-DSA-Python/69a467d94f32c49c1206c19f40d1842e7b5e7235/W3_L4_Operations.png -------------------------------------------------------------------------------- /W3_L4_arrays_lists_binarysearch.py: -------------------------------------------------------------------------------- 1 | " Linear search : find the index of the value if present in the list " 2 | def linear_search(lst, value): 3 | for i in range(len(lst)): 4 | if lst[i] == value: 5 | return f"Found at index {i}" 6 | return "Value not found" 7 | # it check all the elements in the list to find the index 8 | # worst cases : if element is at last of the list, element not in list 9 | # in these cases it is inefficient, so we need a better algorithm 10 | 11 | " Binary search : is a technique to find the index of an element in a sorted list " 12 | # Iterative Binary Search Function 13 | def binary_search_i(arr, x): 14 | low = 0 15 | high = len(arr) - 1 16 | mid = 0 17 | while low <= high: 18 | mid = (high + low) // 2 19 | # If x is greater, ignore left half 20 | if arr[mid] < x: 21 | low = mid + 1 22 | # If x is smaller, ignore right half 23 | elif arr[mid] > x: 24 | high = mid - 1 25 | # means x is present at mid 26 | else: 27 | return mid 28 | # If we reach here, then the element was not present 29 | return -1 30 | 31 | 32 | # Recursive binary search. 33 | def binary_search(arr, low, high, x): 34 | # Check base case 35 | if high >= low: 36 | mid = (high + low) // 2 37 | # If element is present at the middle itself 38 | if arr[mid] == x: 39 | return mid 40 | # If element is smaller than mid, then it can only 41 | # be present in left subarray 42 | elif arr[mid] > x: 43 | return binary_search(arr, low, mid - 1, x) 44 | # Else the element can only be present in right subarray 45 | else: 46 | return binary_search(arr, mid + 1, high, x) 47 | else: 48 | # Element is not present in the array 49 | return -1 50 | 51 | " Python's built in 'list' are not array " -------------------------------------------------------------------------------- /W3_L5_efficiency.py: -------------------------------------------------------------------------------- 1 | """ Efficiency : Measure of time taken by an algorithm 2 | as a function T(n) with respect to input size n """ 3 | 4 | """ Big O notation 5 | T(n) = O(n) 6 | less time order: 7 | log(n) < n < n*log(n) < n**2 < n**3 < 2**n < n! """ 8 | 9 | # Linear search efficiency is : O(n) for any lists 10 | # Binary search efficiency is : O(log(n)) for sorted lists 11 | 12 | " Python can do about 10**7 steps in 1 second" 13 | 14 | " use command : 'time python3 W3_L3_efficiency.py' to get time taken in LINUX" -------------------------------------------------------------------------------- /W3_L6_selection_sort.py: -------------------------------------------------------------------------------- 1 | """ Selection sort """ 2 | 3 | def selection_sort(lst): 4 | n = len(lst) 5 | for i in range(n): 6 | min_index = i 7 | 8 | for j in range(i+1, n): 9 | if lst[j] < lst[min_index]: 10 | min_index = j 11 | lst[i], lst[min_index] = lst[min_index], lst[i] 12 | return lst 13 | 14 | # Time Complexity: O(n**2) 15 | # Auxiliary Space: O(1) 16 | -------------------------------------------------------------------------------- /W3_L7_insertion_bubble_sort.py: -------------------------------------------------------------------------------- 1 | """ Bubble sort """ 2 | def bubble_sort(arr): 3 | n = len(arr) 4 | for i in range(n-1): 5 | for j in range(n-i-1): 6 | if arr[j] > arr[j+1]: 7 | arr[j], arr[j+1] = arr[j+1], arr[j] 8 | return arr 9 | # Time Complexity: O(n**2) 10 | # Auxiliary Space: O(1) 11 | 12 | """ Insertion sort """ 13 | def insertion_sort(arr): 14 | for i in range(1, len(arr)): 15 | key = arr[i] 16 | j = i-1 17 | while j >= 0 and key < arr[j]: 18 | arr[j+1] = arr[j] 19 | j -= 1 20 | arr[j+1] = key 21 | return arr 22 | # Time Complexity: O(n**2) 23 | # Auxiliary Space: O(1) 24 | 25 | """ to set recursion limit to 2000 """ 26 | import sys 27 | sys.setrecursionlimit(2000) 28 | 29 | 30 | """ Insertion using recursion """ 31 | def insertionSortRecursive(arr, n): 32 | # base case 33 | if n <= 1: 34 | return # None 35 | 36 | # Recursively sort first n-1 elements 37 | insertionSortRecursive(arr, n-1) 38 | 39 | # Insert last element at its correct position in sorted array 40 | key = arr[n-1] 41 | j = n-2 42 | while j >= 0 and arr[j] > key: 43 | arr[j+1] = arr[j] 44 | j = j-1 45 | arr[j+1] = key 46 | 47 | # Time Complexity: O(n**2) 48 | # Auxiliary Space: O(n) 49 | 50 | a = [100, 14, 23, 44, 2 ,1] 51 | insertionSortRecursive(a, len(a)) 52 | print(a) 53 | 54 | """ Selection sort and Insertion sort are both O(n**2) 55 | O(n**2) sorting is infeasible for n over 5000 56 | Among insertion sort and selection sort, insertion sort is usually better """ -------------------------------------------------------------------------------- /W3_L8_recursion.py: -------------------------------------------------------------------------------- 1 | ''' Many arithmetic functions are naturally defined inductively ''' 2 | 3 | # FACTORIAL : n! = n x (n-1) x (n-2)! 4 | # n! = n x (n-1)! 5 | def factorial(n): 6 | if n <= 1: 7 | return 1 8 | return n * factorial(n-1) 9 | 10 | # MULTIPLICATION (repeated addition): 11 | # 2 x 3 = 2 + 2 + 2 or 3 + 3 12 | # m x n = m + (m x (n-1)) 13 | def multiplication(m, n): 14 | if n == 1: 15 | return m 16 | return m + multiplication(m, n-1) 17 | 18 | # FIBONACCI SERIES: 19 | # 1 1 2 3 5 8 13 21 34 ... 20 | # fib(1) = fib(2) = 1, fib(3) = fib(1) + fib(2), 21 | # fib(4) = fib(2) + fib(3) 22 | # fib(n) = fib(n-1) + fib(n-2) 23 | def fibonacci(n): 24 | if n <= 1: 25 | return n 26 | return fibonacci(n-1) + fibonacci(n-2) 27 | 28 | """ Inductive definitions naturally give rise to recursive programs """ 29 | # like FACTORIAL, MULTIPLICATION, FIBONACCI etc 30 | 31 | # LENGTH OF LIST using recursion 32 | # length(l) = 1 + length(l[1:]) 33 | def length_list(arr): 34 | if arr == []: 35 | return 0 36 | return 1 + length_list(arr[1:]) 37 | 38 | # SUM OF ELEMENTS IN LIST using recursion 39 | # sum_of(l) = l[0] + sum_of(l[1:]) 40 | def sum_of_list(arr): 41 | if arr == []: 42 | return 0 43 | return arr[0] + sum_of_list(arr[1:]) 44 | 45 | """ 'RecursionError: maximum recursion depth exceeded in comparison' 46 | recursion limit is there in python so that the program won't 47 | run forever if the base case is incorrect ( like in case of infinite loop ) """ 48 | # upto 997 something it's okay but from 998 the above will be alerted (Recursion Error) 49 | # therefore 998 is the default recursion limit in python 50 | 51 | """ We can get over it by setting the recursion Limit """ 52 | import sys 53 | sys.setrecursionlimit(1000) 54 | 55 | -------------------------------------------------------------------------------- /W3_programming_assignment.py: -------------------------------------------------------------------------------- 1 | """ 1) Write a function contracting(l) that takes as input a list of integer l and returns 2 | True if the absolute difference between each adjacent pair of elements strictly decreases. 3 | 4 | Here are some examples of how your function should work. 5 | 6 | >>> contracting([9,2,7,3,1]) 7 | True 8 | 9 | >>> contracting([-2,3,7,2,-1]) 10 | False 11 | 12 | >>> contracting([10,7,4,1]) 13 | False""" 14 | 15 | def contracting(l): 16 | diff= abs(l[1] - l[0]) 17 | for i in range(2, len(l)): 18 | if abs(l[i] - l[i-1]) < diff: 19 | diff = abs(l[i] - l[i-1]) 20 | else: 21 | return False 22 | return True 23 | 24 | """ 2)In a list of integers l, the neighbours of l[i] are l[i-1] and l[i+1]. l[i] is a hill 25 | if it is strictly greater than its neighbours and a valley if it is strictly less than its neighbours. 26 | Write a function counthv(l) that takes as input a list of integers l and returns a list [hc,vc] 27 | where hc is the number of hills in l and vc is the number of valleys in l. 28 | 29 | Here are some examples to show how your function should work. 30 | 31 | >>> counthv([1,2,1,2,3,2,1]) 32 | [2, 1] 33 | 34 | >>> counthv([1,2,3,1]) 35 | [1, 0] 36 | 37 | >>> counthv([3,1,2,3]) 38 | [0, 1]""" 39 | 40 | def counthv(l): 41 | hc, vc = 0, 0 42 | for i in range(1, len(l)-1): 43 | if l[i] > l[i-1] and l[i] > l[i+1]: 44 | hc += 1 45 | elif l[i] < l[i-1] and l[i] < l[i+1]: 46 | vc += 1 47 | return [hc, vc] 48 | 49 | """ 3) A square n×n matrix of integers can be written in Python as a list with n elements, 50 | where each element is in turn a list of n integers, representing a row of the matrix. For instance, the matrix 51 | 52 | 1 2 3 53 | 4 5 6 54 | 7 8 9 55 | would be represented as [[1,2,3], [4,5,6], [7,8,9]]. 56 | 57 | Write a function leftrotate(m) that takes a list representation m of a square matrix as input, and returns the matrix obtained by rotating the original matrix counterclockwize by 90 degrees. For instance, if we rotate the matrix above, we get 58 | 59 | 3 6 9 60 | 2 5 8 61 | 1 4 7 62 | Your function should not modify the argument m provided to the function rotate(). 63 | 64 | Here are some examples of how your function should work. 65 | 66 | >>> leftrotate([[1,2],[3,4]]) 67 | [[2, 4], [1, 3]] 68 | 69 | >>> leftrotate([[1,2,3],[4,5,6],[7,8,9]]) 70 | [[3, 6, 9], [2, 5, 8], [1, 4, 7]] 71 | 72 | >>> leftrotate([[1,1,1],[2,2,2],[3,3,3]]) 73 | [[1, 2, 3], [1, 2, 3], [1, 2, 3]]""" 74 | 75 | def leftrotate(m): 76 | l = len(m) 77 | arr = [[0 for i in range(l)] for i in range(l)] 78 | for i in range(l): 79 | for j in range(l): 80 | arr[i][j] = m[j][(l-1)-i] 81 | return arr 82 | 83 | # def leftrotate(m): 84 | # l = len(m) 85 | # arr = [] 86 | # for i in range(l): 87 | # sub_arr = [] 88 | # for j in range(l): 89 | # sub_arr.append(m[j][(l-1)-i]) 90 | # arr.append(sub_arr) 91 | # return arr 92 | -------------------------------------------------------------------------------- /W4_L1_merge_sort.py: -------------------------------------------------------------------------------- 1 | # Python program for implementation of MergeSort 2 | 3 | # Merges two subarrays of arr[]. 4 | # First subarray is arr[l..m] 5 | # Second subarray is arr[m+1..r] 6 | 7 | 8 | def merge(arr, l, m, r): 9 | n1 = m - l + 1 10 | n2 = r - m 11 | 12 | # create temp arrays 13 | L = [0] * (n1) 14 | R = [0] * (n2) 15 | 16 | # Copy data to temp arrays L[] and R[] 17 | for i in range(0, n1): 18 | L[i] = arr[l + i] 19 | 20 | for j in range(0, n2): 21 | R[j] = arr[m + 1 + j] 22 | # or do L = arr[l:m+1] 23 | # R = arr[m+1:r+1] 24 | # Merge the temp arrays back into arr[l..r] 25 | i = 0 # Initial index of first subarray 26 | j = 0 # Initial index of second subarray 27 | k = l # Initial index of merged subarray 28 | 29 | while i < n1 and j < n2: 30 | if L[i] <= R[j]: 31 | arr[k] = L[i] 32 | i += 1 33 | else: 34 | arr[k] = R[j] 35 | j += 1 36 | k += 1 37 | 38 | # Copy the remaining elements of L[], if there 39 | # are any 40 | while i < n1: 41 | arr[k] = L[i] 42 | i += 1 43 | k += 1 44 | 45 | # Copy the remaining elements of R[], if there 46 | # are any 47 | while j < n2: 48 | arr[k] = R[j] 49 | j += 1 50 | k += 1 51 | 52 | # l is for left index and r is right index of the 53 | # sub-array of arr to be sorted 54 | 55 | 56 | def mergeSort(arr, l, r): 57 | if l < r: 58 | 59 | # Same as (l+r)//2, but avoids overflow for 60 | # large l and h 61 | m = l+(r-l)//2 62 | 63 | # Sort first and second halves 64 | mergeSort(arr, l, m) 65 | mergeSort(arr, m+1, r) 66 | merge(arr, l, m, r) 67 | 68 | 69 | # Driver code to test above 70 | arr = [12, 11, 13, 5, 6, 7] 71 | n = len(arr) 72 | print("Given array is") 73 | print(arr) 74 | 75 | mergeSort(arr, 0, n-1) 76 | print("\n\nSorted array is") 77 | print(arr) 78 | 79 | # Time Complexity: O(n*log(n)) 80 | # Auxiliary Space: O(n) 81 | 82 | """ Limitations """ 83 | # It requires the use of an extra array 84 | # Extra storage can be costly 85 | # Recursive call and return are expensive 86 | -------------------------------------------------------------------------------- /W4_L3_quick_sort.py: -------------------------------------------------------------------------------- 1 | # Python program for implementation of Quicksort Sort 2 | 3 | # This implementation utilizes pivot as the last element in the nums list 4 | # It has a pointer to keep track of the elements smaller than the pivot 5 | # At the very end of partition() function, the pointer is swapped with the pivot 6 | # to come up with a "sorted" nums relative to the pivot 7 | 8 | 9 | def partition(l, r, nums): 10 | # Last element will be the pivot and the first element the pointer 11 | pivot, ptr = nums[r], l 12 | for i in range(l, r): 13 | if nums[i] <= pivot: 14 | # Swapping values smaller than the pivot to the front 15 | nums[i], nums[ptr] = nums[ptr], nums[i] 16 | ptr += 1 17 | # Finally swapping the last element with the pointer indexed number 18 | nums[ptr], nums[r] = nums[r], nums[ptr] 19 | return ptr 20 | 21 | # With quicksort() function, we will be utilizing the above code to obtain the pointer 22 | # at which the left values are all smaller than the number at pointer index and vice versa 23 | # for the right values. 24 | 25 | 26 | def quicksort(l, r, nums): 27 | if len(nums) == 1: # Terminating Condition for recursion. VERY IMPORTANT! 28 | return nums 29 | if l < r: 30 | pi = partition(l, r, nums) 31 | quicksort(l, pi-1, nums) # Recursively sorting the left values 32 | quicksort(pi+1, r, nums) # Recursively sorting the right values 33 | return nums 34 | 35 | 36 | example = [4, 5, 1, 2, 3] 37 | result = [1, 2, 3, 4, 5] 38 | print(quicksort(0, len(example)-1, example)) 39 | 40 | example = [2, 5, 6, 1, 4, 6, 2, 4, 7, 8] 41 | result = [1, 2, 2, 4, 4, 5, 6, 6, 7, 8] 42 | # As you can see, it works for duplicates too 43 | print(quicksort(0, len(example)-1, example)) 44 | 45 | # Time Complexity: Worst case time complexity is O(n**2) and average case time complexity is O(n*log(n)) 46 | # Auxiliary Space: O(1) 47 | 48 | # Worst case is when the array is already sorted 49 | """ Generally built-in sort function in programming languages are quick sort """ 50 | -------------------------------------------------------------------------------- /W4_L5_tuples_dictionaries.py: -------------------------------------------------------------------------------- 1 | """ Tuples """ 2 | # simultaneous assignment 3 | a, b, c = 10, 11, 12 4 | # is actually (a, b, c) = (10, 11, 12) 5 | t = 1, 2, 3 6 | print(type(t)) 7 | 8 | point = (10, 20) # tuple 9 | x_coordinate = point[0] 10 | y_coordinate = point[1] 11 | print(x_coordinate, y_coordinate) 12 | 13 | # But tuples are immutable, we can't do point[0] = 100 14 | 15 | " Dictionary " 16 | empty_dict = {} 17 | empty_dict["One"] = 1 # assigning key value pair 18 | empty_dict["Two"] = 2 19 | print(empty_dict) 20 | # Key should be immutable (int, float, bool, string, tuple) 21 | # Value can be mutable or immutable 22 | 23 | match = {"m1":{"p1":100, "p2":95}, "m2":{"p1":104, "p2":321}} 24 | print(match["m1"]["p2"]) 25 | print(empty_dict.keys()) 26 | -------------------------------------------------------------------------------- /W4_L6_function_definitions.py: -------------------------------------------------------------------------------- 1 | def power(a, b): 2 | ans = 1 3 | for i in range(b): 4 | ans *= a 5 | return ans 6 | 7 | # passing values to function 8 | print(power(2, 5)) 9 | # passing arguments by name 10 | print(power(b=5, a=2)) 11 | 12 | def add(a, b=0): # default argument 13 | return a + b 14 | 15 | print(add(5)) 16 | 17 | def square(x): 18 | return x * x 19 | 20 | def apply(f, x, n): 21 | res = x 22 | for i in range(n): 23 | res = f(res) 24 | return(res) 25 | 26 | print(apply(square, 5, 2)) 27 | # same as square(square(5)) -------------------------------------------------------------------------------- /W4_L7_list_comprehension.py: -------------------------------------------------------------------------------- 1 | # map(f, l) applies f function to every element in l 2 | l = list(map(int, ['1', '2', '3'])) 3 | print(l) 4 | # using list comprenhension 5 | ll = [int(i) for i in '123'] 6 | print(ll) 7 | 8 | # filter(p, l) checks p for each element in l 9 | 10 | def is_even(n): 11 | if n % 2 == 0: 12 | return True 13 | return False 14 | 15 | even_l = list(filter(is_even, [0, 1, 2, 3, 5, 6])) 16 | print(even_l) 17 | # using list comprenhension 18 | even_ll = [i for i in [0, 1, 2, 3, 5, 6] if is_even(i)] 19 | print(even_ll) 20 | 21 | # map() and filter() together 22 | 23 | def square(n): 24 | return n * n 25 | 26 | even_square = list(map(square, filter(is_even, [1, 2, 3, 4, 5, 6]))) 27 | print(even_square) 28 | # using list comprenhension 29 | even_square_l = [i*i for i in [1, 2, 3, 4, 5, 6] if is_even(i)] 30 | print(even_square_l) 31 | print() 32 | 33 | """ Find all (x, y, z) where x*x + y*y = z*z (pythagoras) """ 34 | py = [(x, y, z) for x in range(1, 10) for y in range(1, 10) for z in range(1, 10) if (x*x + y*y)==z*z] 35 | for i in py: 36 | print(i) 37 | print() 38 | # Better code...remove duplicates in x and y 39 | py = [(x, y, z) for x in range(1, 10) for y in range(x, 10) for z in range(y, 10) if x*x + y*y == z*z] 40 | for i in py: 41 | print(i) 42 | 43 | """ 2 D lists """ 44 | lst = [[0 for i in range(3)] for j in range(2)] 45 | print(lst) -------------------------------------------------------------------------------- /W4_programming_assignment.py: -------------------------------------------------------------------------------- 1 | """ 1) Write a Python function frequency(l) that takes as input a list of integers and 2 | returns a pair of the form (minfreqlist,maxfreqlist) where, 3 | minfreqlist is a list of numbers with minimum frequency in l, sorted in ascending order 4 | maxfreqlist is a list of numbers with maximum frequency in l, sorted in ascending order 5 | Here are some examples of how your function should work. 6 | 7 | >>> frequency([13,12,11,13,14,13,7,11,13,14,12]) 8 | ([7], [13]) 9 | 10 | >>> frequency([13,12,11,13,14,13,7,11,13,14,12,14,14]) 11 | ([7], [13, 14]) 12 | 13 | >>> frequency([13,12,11,13,14,13,7,11,13,14,12,14,14,7]) 14 | ([7, 11, 12], [13, 14])""" 15 | 16 | def frequency(l): 17 | l.sort() 18 | data = {} 19 | for i in l: 20 | data[i] = data.get(i, 0) + 1 21 | minimun, maximun = min(data.values()), max(data.values()) 22 | l = ([], []) 23 | for i in data: 24 | if data[i] == minimun: 25 | l[0].append(i) 26 | if data[i] == maximun: 27 | l[1].append(i) 28 | return l 29 | 30 | """ 2)An airline has assigned each city that it serves a unique numeric code. 31 | It has collected information about all the direct flights it operates, represented 32 | as a list of pairs of the form (i,j), where i is the code of the starting city and 33 | j is the code of the destination. 34 | 35 | It now wants to compute all pairs of cities connected by one intermediate hope — city i 36 | is connected to city j by one intermediate hop if there are direct flights of the 37 | form (i,k) and (k,j) for some other city k. The airline is only interested in 38 | one hop flights between different cities — pairs of the form (i,i) are not useful. 39 | 40 | Write a Python function onehop(l) that takes as input a list of pairs representing 41 | direct flights, as described above, and returns a list of all pairs (i,j), where i != j, 42 | such that i and j are connected by one hop. Note that it may already be the case that 43 | there is a direct flight from i to j. So long as there is an intermediate k with a flight 44 | from i to k and from k to j, the list returned by the function should include (i,j). 45 | The input list may be in any order. The pairs in the output list should be in 46 | lexicographic (dictionary) order. Each pair should be listed exactly once. 47 | 48 | Here are some examples of how your function should work. 49 | 50 | >>> onehop([(2,3),(1,2)]) 51 | [(1, 3)] 52 | 53 | >>> onehop([(2,3),(1,2),(3,1),(1,3),(3,2),(2,4),(4,1)]) 54 | [(1, 2), (1, 3), (1, 4), (2, 1), (3, 2), (3, 4), (4, 2), (4, 3)] 55 | 56 | >>> onehop([(1,2),(3,4),(5,6)]) 57 | [] """ 58 | 59 | def onehop(l): 60 | result=[] 61 | n = len(l) 62 | for i in range(n): 63 | for j in range(n): 64 | if l[i][1] == l[j][0] and l[i][0] != l[j][1]: 65 | result.append((l[i][0], l[j][1])) 66 | return sorted(set(result)) 67 | -------------------------------------------------------------------------------- /W4_quiz.py: -------------------------------------------------------------------------------- 1 | # 1) 2 | def mystery(l): 3 | if l == []: 4 | return(l) 5 | else: 6 | return(mystery(l[1:])+l[:1]) 7 | 8 | print(mystery([22,14,19,65,82,55])) 9 | 10 | # 2) 11 | pairs = [ (x,y) for x in range(4,1,-1) for y in range(5,1,-1) if (x+y)%3 == 0 ] 12 | print(pairs) 13 | 14 | # 3) 15 | wickets = {"Tests":{"Bumrah":[3,5,2,3],"Shami":[4,4,1,0],"Ashwin":[2,1,7,4]},"ODI":{"Bumrah":[2,0],"Shami":[1,2]}} 16 | # wickets["ODI"]["Ashwin"][0:] = [4,4] Error 17 | # wickets["ODI"]["Ashwin"].extend([4,4]) Error 18 | wickets["ODI"]["Ashwin"] = [4,4] 19 | # wickets["ODI"]["Ashwin"] = wickets["ODI"]["Ashwin"] + [4,4] Error 20 | 21 | # 4) 22 | hundreds = {} 23 | # hundreds["Tendulkar, international"] = 100 24 | # hundreds["Tendulkar"] = {"international":100} 25 | # hundreds[("Tendulkar","international")] = 100 26 | hundreds[["Tendulkar","international"]] = 100 -------------------------------------------------------------------------------- /W5_L1_exception_handling.py: -------------------------------------------------------------------------------- 1 | # Handling error is exception handling 2 | 3 | try: 4 | print(5/0) 5 | except Exception as e: 6 | print("Error") 7 | print(e) 8 | 9 | """ 10 | try: 11 | # Some Code.... 12 | except: 13 | # optional block 14 | # Handling of exception (if required) 15 | else: 16 | # execute if no exception 17 | finally: 18 | # Some code .....(always executed) 19 | """ -------------------------------------------------------------------------------- /W5_L2_standard_input_output.py: -------------------------------------------------------------------------------- 1 | """ Programs need to interact with user 2 | - receive input (Keyboard) 3 | - display output (Screen) """ 4 | 5 | # Read a line of input and assign it to variable data 6 | data = input("Enter the data : ") 7 | # default datatype is string 8 | data = int(input("Enter the number : ")) 9 | # the datatype is converted to integer 10 | 11 | while True: 12 | try: 13 | value = int(input("Enter the number : ")) 14 | except: 15 | print("The entered data is not an interger. Try again...") 16 | else: 17 | break 18 | 19 | print() 20 | print("A line", end=" ") 21 | print("Same line", end="\nNext line\n") 22 | print(1, 2, 3, 4, 5, sep="-") 23 | -------------------------------------------------------------------------------- /W5_L3_file1.txt: -------------------------------------------------------------------------------- 1 | Hey New World 2 | New line here 3 | Another new line hereline 1 4 | line 2 5 | line 3 6 | -------------------------------------------------------------------------------- /W5_L3_handling_files.py: -------------------------------------------------------------------------------- 1 | with open("W5_L3_file1.txt", "w") as f: 2 | f.write("Hey New World\n") 3 | f.write("New line here\n") 4 | f.write("Another new line here") 5 | # f.write() - to write to a file, return number of characters written 6 | f.writelines(["line 1\n", "line 2\n", "line 3\n"]) 7 | # f.writelines() - to write a list of lines 8 | 9 | # r - mode to read a file 10 | # w - mode to write to a file 11 | # a - mode to append a file 12 | # f.close() - flushes the buffer if opened used f.open() 13 | # with open() as f - if used don't need to close using f.close() 14 | 15 | with open("W5_L3_file1.txt") as f: 16 | print(f.read()) 17 | print() 18 | # f.read() - read the entire data in a string format 19 | # f.read(n) - read the n characters of the file 20 | 21 | with open("W5_L3_file1.txt") as f: 22 | print(f.readline(), end="") 23 | print(f.readline()) 24 | # f.readline() - read the next line in string format 25 | with open("W5_L3_file1.txt") as f: 26 | print(f.readlines()) 27 | # f.readlines() - read and give a list of each line as string 28 | with open("W5_L3_file1.txt") as f: 29 | for i in f.readlines(): 30 | print(i[:-1]) # will exclude '\n' in the end 31 | 32 | """ f.seek() can be used to move the file poiner to any position """ 33 | # f.seek(0) - to move file pointer to begining of the file -------------------------------------------------------------------------------- /W5_L4_string_functions.py: -------------------------------------------------------------------------------- 1 | """ lstrip() - strip the empty spaces in left """ 2 | a = " joswin" 3 | print(a) # joswin 4 | print(a.lstrip()) # joswin 5 | 6 | """ rstrip() - strip the empty spaces in right """ 7 | 8 | """ strip() - strip off the empty spaces in both sides """ 9 | a = " the best " 10 | print(a) # the best 11 | print(a.strip()) # the best 12 | 13 | """ find(s) - return the first occurance of s in the string """ 14 | a = "apple" 15 | print(a.find("p")) # 1 16 | print(a.find("pl")) # 2 17 | 18 | """ index(s) - as same as find(s) but raise ValueError if s not found """ 19 | a = "apple" 20 | print(a.index("ppl")) # 1 21 | 22 | """ replace(fromstr, tostr) - replace the fromstr with tostr in the string """ 23 | a = "apples is healthy is" 24 | print(a.replace("is", "are")) # apples are healthy are 25 | print(a.replace("is", "are", 1)) # apples are healthy is 26 | 27 | """ split(s) - split a string on s into list elements """ 28 | a = "one,two,three,four" 29 | print(a.split(",")) # ['one', 'two', 'three', 'four'] 30 | print(a.split(",", 2)) # ['one', 'two', 'three,four'] 31 | 32 | """ join(l) - recombines a list of string using a separator """ 33 | a = ["1", "2", "3", "4"] 34 | print(",".join(a)) # 1,2,3,4 35 | 36 | """ capitalize() - return new string with first letter uppercase rest lower """ 37 | """ lower() - return new string with all letter lowercase """ 38 | """ upper() - return new string with all letter uppercase """ 39 | """ title() - return title format, first letter of each word in sentence capital """ 40 | a = "this world is happy" 41 | print(a.capitalize()) # This world is happy 42 | print(a.title()) # This World Is Happy 43 | 44 | """ swapcase() - revert uppercase to lower and lower to upper """ 45 | 46 | """ center(n) - return string of length n with s centred with blank """ 47 | print("joswin".center(10, "*")) # **joswin** 48 | print("joswin".ljust(10, "*")) # joswin**** 49 | print("joswin".rjust(10, "*")) # ****joswin 50 | 51 | 52 | """ isalpha() - checks if the string is alphabitical """ 53 | """ isnumeric() - checks if the stirng is numerical """ -------------------------------------------------------------------------------- /W5_L5_formatting_printed_output.py: -------------------------------------------------------------------------------- 1 | print("Joswin", "is", "the", "best", sep="$", end="(^_^)\n") 2 | 3 | """ format() method """ 4 | print("First : {0}, Second : {1}".format("Joswin", 100)) 5 | # First : Joswin, Second : 100 6 | print("First : {1}, Second : {0}".format("Joswin", 100)) 7 | # First : 100, Second : Joswin 8 | print("First : {a}, Second : {f}".format(a="Joswin", f=99)) 9 | # First : Joswin, Second : 99 10 | 11 | print("Value : {0:3d}".format(10)) 12 | # Value : 10 13 | print("Value : {0:7.3f}".format(55.223433)) 14 | # Value : 55.223 -------------------------------------------------------------------------------- /W5_L6_pass_del_None.py: -------------------------------------------------------------------------------- 1 | """ pass - is used to do noting or leave an inteded block blank """ 2 | if True: 3 | pass 4 | else: 5 | pass 6 | 7 | """ del """ 8 | lst = [1, 2, 3, 4, 5] 9 | del(lst[2]) 10 | print(lst) # [1, 2, 4, 5] 11 | 12 | dic = {1:"one", 2:"two"} 13 | del(dic[1]) 14 | print(dic) # {2: 'two'} 15 | 16 | x = 10 17 | print(x) # 10 18 | del(x) 19 | #print(x) # Error , del(x) makes x undefined 20 | 21 | """ None - is a keyword used to denote 'nothing' """ -------------------------------------------------------------------------------- /W5_programming_assignment.py: -------------------------------------------------------------------------------- 1 | """ Q) Here are some basic facts about tennis scoring: A tennis match is made up of sets. A set is made up of games. 2 | 3 | To win a set, a player has to win 6 games with a difference of 2 games. At 6-6, there is often a special tie-breaker. In some cases, players go on playing till 4 | one of them wins the set with a difference of two games. Tennis matches can be either 3 sets or 5 sets. The player who wins a majority of sets wins the match 5 | (i.e., 2 out 3 sets or 3 out of 5 sets) The score of a match lists out the games in each set, with the overall winner's score reported first for each set. 6 | Thus, if the score is 6-3, 5-7, 7-6 it means that the first player won the first set by 6 games to 3, lost the second one 5 games to 7 and won the 7 | third one 7 games to 6 (and hence won the overall match as well by 2 sets to 1). 8 | You will read input from the keyboard (standard input) containing the results of several tennis matches. Each match's score is recorded on a separate line with the 9 | following format: 10 | 11 | Winner:Loser:Set-1-score,...,Set-k-score, where 2 ≤ k ≤ 5 12 | 13 | For example, an input line of the form 14 | 15 | Osaka:Barty:3-6,6-3,6-3 16 | indicates that Osaka beat Barty 3-6, 6-3, 6-3 in a best of 3 set match. 17 | 18 | The input is terminated by a blank line. 19 | 20 | You have to write a Python program that reads information about all the matches and compile the following statistics for each player: 21 | 22 | Number of best-of-5 set matches won 23 | Number of best-of-3 set matches won 24 | Number of sets won 25 | Number of games won 26 | Number of sets lost 27 | Number of games lost 28 | You should print out to the screen (standard output) a summary in decreasing order of ranking, where the ranking is according to the criteria 1-6 in that order 29 | (compare item 1, if equal compare item 2, if equal compare item 3 etc, noting that for items 5 and 6 the comparison is reversed). 30 | 31 | For instance, given the following data 32 | 33 | Zverev:Medvedev:2-6,6-7,7-6,6-3,6-1 34 | Barty:Osaka:6-4,6-4 35 | Medvedev:Zverev:6-3,6-3 36 | Osaka:Barty:1-6,7-5,6-2 37 | Zverev:Medvedev:6-0,7-6,6-3 38 | Osaka:Barty:2-6,6-2,6-0 39 | Medvedev:Zverev:6-3,4-6,6-3,6-4 40 | Barty:Osaka:6-1,3-6,7-5 41 | Zverev:Medvedev:7-6,4-6,7-6,2-6,6-2 42 | Osaka:Barty:6-4,1-6,6-3 43 | Medvedev:Zverev:7-5,7-5 44 | Osaka:Barty:3-6,6-3,6-3 45 | your program should print out the following 46 | 47 | Zverev 3 0 10 104 11 106 48 | Medvedev 1 2 11 106 10 104 49 | Osaka 0 4 9 76 8 74 50 | Barty 0 2 8 74 9 76 51 | You can assume that there are no spaces around the punctuation marks ":", "-" and ",". Each player's name will be spelled consistently and no two players 52 | have the same name.""" 53 | 54 | """CODE""" 55 | 56 | data=input() 57 | result={} 58 | while data: 59 | winner, loser, sets = data.split(':') 60 | winner_sets, loser_sets = 0, 0 61 | winner_games, loser_games = 0, 0 62 | winner_BO5, winner_BO3 = 0, 0 63 | 64 | for i in sets.split(','): 65 | score = list(map(int, i.split('-'))) 66 | 67 | if score[0] > score[1]: 68 | winner_sets += 1 69 | else: 70 | loser_sets += 1 71 | 72 | winner_games += score[0] 73 | loser_games += score[1] 74 | 75 | if winner_sets >= 3: 76 | winner_BO5 += 1 77 | else: 78 | winner_BO3 += 1 79 | 80 | if winner not in result: 81 | result[winner] = [0, 0, 0, 0, 0, 0] 82 | if loser not in result: 83 | result[loser] = [0, 0, 0, 0, 0, 0] 84 | 85 | result[winner][0] += winner_BO5 86 | result[winner][1] += winner_BO3 87 | result[winner][2] += winner_sets 88 | result[winner][3] += winner_games 89 | result[winner][4] += loser_sets 90 | result[winner][5] += loser_games 91 | 92 | result[loser][2] += loser_sets 93 | result[loser][3] += loser_games 94 | result[loser][4] += winner_sets 95 | result[loser][5] += winner_games 96 | 97 | data=input() 98 | 99 | result = sorted(result.items(), key=lambda t:t[1], reverse=True) 100 | 101 | for player in result: 102 | print(f"{player[0]} {player[1][0]} {player[1][1]} {player[1][2]} {player[1][3]} {player[1][4]} {player[1][5]}") 103 | -------------------------------------------------------------------------------- /W6_L1_PythonCode.py: -------------------------------------------------------------------------------- 1 | def initialize(n): 2 | for key in ['queen','row','col','nwtose','swtone']: 3 | board[key] = {} 4 | for i in range(n): 5 | board['queen'][i] = -1 6 | board['row'][i] = 0 7 | board['col'][i] = 0 8 | for i in range(-(n-1),n): 9 | board['nwtose'][i] = 0 10 | for i in range(2*n-1): 11 | board['swtone'][i] = 0 12 | 13 | def printboard(): 14 | for row in sorted(board['queen'].keys()): 15 | print((row,board['queen'][row]),end=" ") 16 | print("") 17 | 18 | def free(i,j): 19 | return(board['row'][i] == 0 and board['col'][j] == 0 and 20 | board['nwtose'][j-i] == 0 and board['swtone'][j+i] == 0) 21 | 22 | def addqueen(i,j): 23 | board['queen'][i] = j 24 | board['row'][i] = 1 25 | board['col'][j] = 1 26 | board['nwtose'][j-i] = 1 27 | board['swtone'][j+i] = 1 28 | 29 | def undoqueen(i,j): 30 | board['queen'][i] = -1 31 | board['row'][i] = 0 32 | board['col'][j] = 0 33 | board['nwtose'][j-i] = 0 34 | board['swtone'][j+i] = 0 35 | 36 | def placequeen(i): 37 | n = len(board['queen'].keys()) 38 | for j in range(n): 39 | if free(i,j): 40 | addqueen(i,j) 41 | if i == n-1: 42 | printboard() 43 | else: 44 | extendsoln = placequeen(i+1) 45 | undoqueen(i,j) 46 | 47 | board = {} 48 | n = int(input("How many queens? ")) 49 | initialize(n) 50 | if placequeen(0): 51 | printboard() -------------------------------------------------------------------------------- /W6_L2_globalscope_nestedfunctions.py: -------------------------------------------------------------------------------- 1 | """ Scope is the portion of the code where the name or variable is 2 | available to read and update """ 3 | 4 | # def f(): 5 | # y = x # Value of x taken from global scope 6 | # # y is in local scope 7 | # print(y) 8 | # x = 7 # Global scope 9 | # f() 10 | 11 | # Output: 7 12 | 13 | # def f(): 14 | # y = x 15 | # print(y) 16 | # x = 22 # we try to assign value to x, which is global, so error 17 | # x = 7 18 | # f() 19 | 20 | # Output: Error 21 | 22 | """ This applies only to immutable values, for mutable values, 23 | we can change the value in functions or local scope """ 24 | 25 | # def f(): 26 | # y = x[0] 27 | # print(y, end=" ") 28 | # x[0] = 22 # trying to change value of mutable datatype(list) 29 | # print(x) 30 | # x = [7] 31 | # f() 32 | 33 | # Output: 7 [22] 34 | 35 | # def f(): 36 | # global x # calling the global x, making it available in local scope to change 37 | # y = x 38 | # print(y, end=" ") 39 | # x = 22 40 | # print(x) 41 | # x = 7 42 | # f() 43 | 44 | # Output: 7 22 45 | 46 | """ Nested Functions """ 47 | # Functions inside another function are nested functions 48 | # These functions are available withing the local scope or the funtion only 49 | 50 | def f(): 51 | a = 10 52 | def g(): 53 | nonlocal a # here we call in the local a, a is available in f() or local scope not global 54 | a = 100 # since we are changing its value, we use nonlocal a, like we used global x, before 55 | return a 56 | def h(): 57 | return 1 58 | print(g() + h()) # g() and h() are the nested funcitons and can be used inside f() only 59 | f() 60 | 61 | # Output: 3 -------------------------------------------------------------------------------- /W6_L3_generating_permutations.py: -------------------------------------------------------------------------------- 1 | # Often used when we need to try out all possibilities 2 | 3 | # For instance, what is the next sequence formed from 4 | # {a, b, c, d, e, f, g, h, i, j, k, l, m}, in dictionary after 5 | # d c h b a e g l k o n m j i 6 | 7 | # smallest permutation - all elements in ascending order 8 | # a b c d e f g h i j k l m 9 | 10 | # largest permutation - all elements in descending order 11 | # m l k j i h g f e d c b a 12 | 13 | # next permutation - find shortest suffix that can be incremented 14 | # or longest suffix that cannot be incremented 15 | 16 | # longest suffix that cannot be incremented - 17 | # - already in descending order 18 | 19 | # d c h b a e g l k o n m j i 20 | 21 | # o n m j i - these letters are in descending order 22 | # so we cannot make any larger permutations 23 | # we fix from d to k - o n m j i is the largest permutation of the suffix 24 | # to increment we also include k, suffix k o n m j i can be incremented 25 | # how do we increment this? 26 | # replace k by next largest letter to its right, m 27 | # d c h b a e g l m o n k j i 28 | # now arrange o n k j i in ascending order - i j k n o 29 | 30 | # d c h b a e g l m i j k n o - is the next permutation 31 | 32 | """ Implementation """ 33 | #1) from the right, identify first decreasing position 34 | # k in - d c h b a e g l k o n m j i 35 | 36 | #2) swap that value with its next larger letter to its right 37 | # m is the next larger letter of k - in o n m j i 38 | # it becomes - d c h b a e g l m o n k j i 39 | 40 | #3) reverse the increasing suffix 41 | # reverse or arrange in ascending order - o n k j i 42 | # to i j k n o 43 | 44 | # d c h b a e g l m i j k n o - is the next permutation -------------------------------------------------------------------------------- /W6_L4_sets_stacks_queues.py: -------------------------------------------------------------------------------- 1 | # Algorithms + Data Structures = Programs 2 | 3 | """ Data structures examples 4 | - arrays / lists 5 | - dictionaries 6 | - python builtin data types etc """ 7 | 8 | """ -----------------------------------------------------------""" 9 | 10 | """ Sets """ 11 | colors = {"Red", "Green", "Blue", "White"} 12 | print(colors) 13 | # {'Green', 'Blue', 'White', 'Red'} 14 | empty_set = {} 15 | print(empty_set) 16 | # {} 17 | print("Green" in colors) 18 | # True 19 | word = "Banana" 20 | print(set(word)) 21 | # {'B', 'a', 'n'} 22 | 23 | """ Set operations """ 24 | odd = {1, 3, 5, 7, 9, 11} 25 | prime = {2, 3, 5, 7, 11} 26 | 27 | """ Union """ 28 | print(odd | prime) # or 29 | # {1, 2, 3, 5, 7, 9, 11} 30 | 31 | """ Intersection """ 32 | print(odd & prime) # and 33 | # {11, 3, 5, 7} 34 | 35 | """ Set difference """ 36 | print(odd - prime) 37 | # {1, 9} 38 | 39 | """ ------------------------------------------------------------- """ 40 | 41 | """ Stacks """ 42 | # LIFO - last in first out 43 | # operations that can be performed: 44 | # push(element) - push an element to the last of the stack 45 | # list.append(element) - can be used 46 | # pop() - pop and element from the last of the stack and return it 47 | # list.pop() - can be used 48 | 49 | """ ------------------------------------------------------------- """ 50 | 51 | """ Queues """ 52 | # FIFO - first in first out 53 | # LILO - last in last out 54 | # operations that can be performed: 55 | # addq(element) - add an element to the last of the queue 56 | # also called enqueue 57 | # list.append(element) - can be used 58 | # removeq() - remove the element from the head of the queue 59 | # also called dequeue 60 | # list.pop(0) - can be used 61 | 62 | """ ------------------------------------------------------------- """ 63 | 64 | -------------------------------------------------------------------------------- /W6_L5_priority_queues_heaps.py: -------------------------------------------------------------------------------- 1 | """ Priority queues """ 2 | # delete_max() - identifies and remove the element with highest priority 3 | # need not be unique 4 | # insert() - add a new element to the list 5 | 6 | # If unsorted list - insert() takes O(1) time 7 | # - delete_max() takes O(n) time 8 | 9 | # If sorted list - delete_max() takes O(1) time 10 | # - insert() takes O(n) time 11 | 12 | # Thus processing a sequence of n cases requires O(n**2) time 13 | 14 | """ Binary tree """ 15 | # a basic Two dimensional structure 16 | # at each node 17 | # - value 18 | # - link to parent, left child, right child 19 | # node with no children is called leaf 20 | # node with children is called parent, have a right child and left child 21 | # Ex 5 (root) 22 | # / \ 23 | #(parent) 2 8 24 | # / \ \ 25 | # 1 4 9 (leaf) 26 | # (left and right child) 27 | 28 | """ Heap """ 29 | # Heaps are a tree based implementation of priority queues 30 | # Heaps are special kind of binary tree having a priority queue 31 | # This tree is a balanced tree 32 | # A balanced tree is the one in which at each point the left and right 33 | # side are almost the same size. 34 | # N node tree has height log(N) 35 | # Both insert and delete_max() take O(log(N)) 36 | # Progessing N cases takes time O(N*log(N)) 37 | # Truly flexible, need not fix upper bound for N in advance 38 | 39 | # Head a binary tree with two properties: 40 | # -Binary tree is filled level by level, left to right 41 | # -At each node, value stored is bigger than both children 42 | # -Max Heap property - evey value in node is bigger than the values of the two children 43 | 44 | # Ex 24 45 | # / \ 46 | # 11 7 47 | # / 48 | # 10 49 | # correct example of a node, 11>10, 24>11 and 24>7 50 | 51 | # Ex 24 52 | # / \ 53 | # 11 7 54 | # / \ / \ 55 | # 10 5 6 5 56 | # correct example of a node, 11>10 and 11>5, 57 | # 7>6 and 7>5, 24>11 and 24>7 58 | 59 | # Ex 24 60 | # / \ 61 | # 11 7 62 | # / \ / 63 | # 10 5 6 64 | # / 65 | # 8 66 | # not a heap, as right child of node with value 7 67 | # is not filled with 8. STRUCTURE of heap is violated 68 | 69 | # Ex 24 70 | # / \ 71 | # 11 7 72 | # / \ / \ 73 | # 10 5 8 5 74 | # not a heap, as 7<8 75 | # Max Heap property is violated, so not a heap 76 | 77 | # insert() 78 | # Ex 24 79 | # / \ 80 | # 11 7 81 | # / \ / \ 82 | # 10 5 6 5 83 | # insert(12) to this node - it will become 84 | # 24 85 | # / \ 86 | # 12 7 87 | # / \ / \ 88 | # 11 5 6 5 89 | # / 90 | # 10 91 | # insert(33) 92 | # 33 93 | # / \ 94 | # 24 7 95 | # / \ / \ 96 | # 12 5 6 5 97 | # / \ 98 | # 10 11 99 | 100 | # Complexity of insert() 101 | # we have to walk up from the leaf to the root - height of tree 102 | # insert() takes time O(log(N)) 103 | 104 | # delete_max() 105 | # maximum value is always the root 106 | # - first, we create a "hole" at the root 107 | # - reducing one value requires deleting last node 108 | # - move the "homeless" value to root 109 | # __ 110 | # / \ 111 | # 24 7 112 | # / \ / \ 113 | # 12 5 6 5 # first step 114 | # / \ 115 | # 10 11 116 | # 11 117 | # / \ 118 | # 24 7 119 | # / \ / \ # next step 120 | # 12 5 6 5 121 | # / 122 | # 10 123 | # - now we have to restore the heap property 124 | # 24 125 | # / \ 126 | # 12 7 127 | # / \ / \ 128 | # 11 5 6 5 129 | # / 130 | # 10 131 | 132 | # will follow a single path from root to leaf 133 | # cost proportional to height of tree 134 | # delete_max() takes time O(log(N)) 135 | 136 | # delete_max() again on the heap 137 | # __ 138 | # / \ 139 | # 12 7 140 | # / \ / \ 141 | # 11 5 6 5 142 | # / 143 | # 10 144 | # 10 145 | # / \ 146 | # 12 7 147 | # / \ / \ 148 | # 11 5 6 5 149 | # 12 150 | # / \ 151 | # 11 7 152 | # / \ / \ 153 | # 10 5 6 5 154 | 155 | # Implementing using an array 156 | # 0 157 | # / \ 158 | # 1 2 159 | # / \ / \ # 0,1,2... represent indexes 160 | # 3 4 5 6 161 | # / \ 162 | # 7 8 163 | 164 | # - number the nodes left to right, level by level 165 | # - represent as an array H[0..N-1] 166 | # - Children of H[i] are at H[2*i + 1] , H[2*i + 2] 167 | # - Parent of H[j] is at H[floor((j-1)/2)] for j > 0 168 | 169 | """ Building a heap, heapify() """ 170 | # give a list of values [x1,x2,x3,x4,...,xn], build a heap 171 | # Naive approach 172 | # - start with an empty heap 173 | # - insert each xj 174 | # - overall O(N*log(N)) 175 | 176 | # Better heapify() 177 | # - Set up the array a [x1,x2,x3,x4,...,xn] 178 | # - Leaf nodes trivially satisy heap property 179 | # - second half of array is already a valid heap 180 | # - Assume leaf nodes are at level k 181 | # - for each node at level k-1, k-2, ... , 0, fix heap property 182 | # - as we go up, the number of steps per node goesup by 1, 183 | # but the number of nodes per level is halved 184 | # - cost turns out to be O(N) overall 185 | 186 | """ Heap sort """ 187 | # - start with an unordered list 188 | # - build a heap - O(n) 189 | # - call delete_max() n times to extract elements in descending order - O(n*log(n)) 190 | # - after each delete_max(), heap shrinks by 1, 191 | # - store maximum value at the end of the current heap 192 | # - in place O(n*log(n)) sort 193 | 194 | """ Heap min """ 195 | # we can revert the heap condition 196 | # each node is smaller than the children 197 | # Min-heap, for insert(), delete_min() -------------------------------------------------------------------------------- /W6_quiz.py: -------------------------------------------------------------------------------- 1 | """ 1) Suppose u and v both have values of type set and are disjoint. 2 | Which of the following expressions evaluates to True? 3 | u == v | (u^v) 4 | u == (v^u) 5 | u == v^(u | v) 6 | u == u^(v | u) """ 7 | 8 | # Disjoin sets are sets that have no common value 9 | # or u intersection v = null set 10 | 11 | # u = {1, 2, 3} 12 | # v = {4, 5, 6} 13 | 14 | # print(u^v) # {1, 2, 3, 4, 5, 6} 15 | # The symmetric difference between two sets is the set of all the elements 16 | # that are either in the first set or the second set but not in both. 17 | 18 | # You have the choice of using either the symmetric_difference() method 19 | # or the ^ operator to do this in Python. 20 | 21 | # print(u == v | (u^v)) # False 22 | # print(u == (v^u)) # False 23 | # print(u == v^(u | v)) # True 24 | # print(u == u^(v | u)) # False 25 | 26 | # ANS) u == v^(u | v) 27 | 28 | """ 2) Suppose u and v both denote sets in Python. What is the most 29 | general condition that guarantees that u|v == u^v? 30 | The sets u and v should be disjoint. 31 | The set u should be a subset of the set v. 32 | The set v should be a subset of the set u. 33 | This is true for any u and v. """ 34 | 35 | # u = {1, 2, 3} # u and v are disjoint sets 36 | # v = {4, 5, 6} 37 | # print(u | v) # {1, 2, 3, 4, 5, 6} 38 | # print(u ^ v) # {1, 2, 3, 4, 5, 6} 39 | # print(u|v == u^v) # True 40 | 41 | # u = {1, 2, 3} # u is a subset of set v 42 | # v = {1, 2, 3, 4, 5} 43 | # print(u | v) # {1, 2, 3, 4, 5} 44 | # print(u ^ v) # {4, 5} 45 | # print(u|v == u^v) # False 46 | 47 | # u = {1, 2, 3, 4, 5} 48 | # v = {1, 2, 3} # v is a subset of set u 49 | # print(u | v) # {1, 2, 3, 4, 5} 50 | # print(u ^ v) # {4, 5} 51 | # print(u|v == u^v) # False 52 | 53 | # Thus, u|v == u^v when u and v are disjoint sets 54 | 55 | # ANS) The sets u and v should be disjoint. 56 | 57 | """ 3) Consider the min-heap [15, 27, 33, 39, 66, 39, 47, 58, 51]. 58 | built by repeatedly inserting values into an empty heap. Which of the 59 | following could not have been the last element inserted into this heap? 60 | 27 61 | 15 62 | 58 63 | 51 """ 64 | # check this out for explanation 65 | # https://stackoverflow.com/questions/69024237/find-the-last-element-inserted-into-the-min-heap 66 | 67 | # ANS) 58 68 | 69 | """ 4) Suppose we execute delete-min twice on the min-heap [13,29,24,67,52,89,45,98,79,58]. 70 | What is the resulting heap? """ 71 | # just do to delete_min() operation on the heap and we will get the answer: 72 | # ANS) [29,52,45,67,79,89,58,98] 73 | -------------------------------------------------------------------------------- /W7_L1_abstractDatatypes_classes_objects.py: -------------------------------------------------------------------------------- 1 | # We can go beyond the builtin datatypes and create ourown datatypes 2 | # A data structure is an organisation of information whose behaviour is 3 | # defined through an interface, interface is the allowed set of operations 4 | # Ex: stack is a data structure having operations pop() and push() 5 | # Ex: queue - having operations enqueue() and dequeue() 6 | # Ex: Heap - insert() and delete_max() 7 | 8 | # We are implementing all these using builtin list, 9 | # but the operations of list, like append should not be done in heap etc 10 | 11 | # In order to avoid this, we define abstract datatypes, where no other operations can be done 12 | # Ex: In stack, (stack.push(v)).pop() == v, as stack.pop() gives back the last value added 13 | # and last value added using stack.push() is v, so it should be equal to v 14 | # Ex: In queue, ((q.enqueue(u)).enqueue(v)).dequeue() == u 15 | 16 | # We should imagine each data structures like a black box 17 | # only specific operations can be done which is unique to each data structure 18 | 19 | # Abstract Data Types can be defined using object oriented programming (OOP) 20 | # Class and Objects are important concepts of OOP 21 | 22 | # Class: is like a template, how data is stored, what all operations can be performed 23 | # Object: is an instance of the template 24 | 25 | # DUMMY EXAMPLE 26 | """ 27 | class Heap: 28 | def __init__(self, l): 29 | # create heap from list l 30 | def insert(self, x): 31 | # insert x into heap 32 | def delete_max(self): 33 | # return max element and delete it from heap 34 | 35 | # creating object 36 | h1 = Heap([15, 32, 10]) 37 | # applying operations 38 | h1.insert(23) 39 | h1.insert(19) 40 | x = h1.delete_max() 41 | """ 42 | -------------------------------------------------------------------------------- /W7_L2_classes_objects_in_Python.py: -------------------------------------------------------------------------------- 1 | # 'self' is a name that is used inside a class to refer to the object 2 | # that we are currently working on 3 | 4 | # __init__() is called the constructor, 5 | # called when object is created 6 | # __str__() return the string representation, 7 | # of the object when called 8 | # str(o) == o.__str__(), implicitly invoked by print() 9 | # __add__() : invoked implicitly by + 10 | # p1 + p2 = p1.__add__(p2) 11 | # __mul__() : called implicitly by * 12 | # p1 * p2 = p1.__mul__(p2) 13 | #__lt__(), __gt__(), __le__() 14 | # called implicityly by <, >, <= 15 | 16 | 17 | """ Class on points on a plane """ 18 | class Point: 19 | 20 | def __init__(self, a, b): 21 | self.x = a 22 | self.y = b 23 | 24 | def __str__(self): 25 | return f"a point where co-ordinates ({self.x}, {self.y})" 26 | 27 | def __add__(self, p2): 28 | return Point(self.x + p2.x, self.y + p2.y) 29 | 30 | def translate(self, deltax, deltay): 31 | # shifting (x, y) to (x+deltax, y+deltay) 32 | self.x += deltax 33 | self.y += deltay 34 | 35 | def o_distance(self): 36 | # return the distance from origin 37 | return (self.x**2 + self.y**2)**(1/2) 38 | 39 | class Point2: 40 | 41 | def __init__(self, a=0, b=0): # a=0, b=0 are default arguments 42 | import math 43 | self.r = math.sqrt(a*a + b*b) 44 | if a == 0: 45 | self.theta = 0 46 | else: 47 | self.theta = math.atan(b/a) 48 | 49 | def o_distance(self): 50 | return self.r 51 | 52 | def translate(self, deltax, deltay): 53 | import math 54 | x = self.r * math.cos(self.theta) 55 | y = self.r * math.sin(self.theta) 56 | x += deltax 57 | y += deltay 58 | self.r = math.sqrt(x*x + y*y) 59 | if x == 0: 60 | self.theta = 0 61 | else: 62 | self.theta = math.atan(y/x) 63 | 64 | p1 = Point(5, 15) 65 | print(p1) 66 | # a point where co-ordinates (5, 15) 67 | print(p1.x, p1.y) 68 | # Output: 5 15 69 | p1.translate(10, 5) 70 | print(p1) 71 | # a point where co-ordinates (15, 20) 72 | print(p1.x, p1.y) 73 | # Output: 15 20 74 | print(p1.o_distance()) 75 | # Output: 25.0 76 | 77 | p = Point(100, 100) 78 | p4 = p + p1 79 | print(p4) 80 | # a point where co-ordinates (115, 120) 81 | 82 | p2 = Point2(15, 20) 83 | print(p2.o_distance()) 84 | # Output: 25.0 85 | p2.translate(5, 5) 86 | # Now point (15, 20) becomes (20, 25) 87 | # also r and theta value also changes 88 | print(p2.o_distance()) 89 | # Output: 32.01562118716424 , which is sqrt(20**2 + 25**2) 90 | -------------------------------------------------------------------------------- /W7_L3_PythonCode.py: -------------------------------------------------------------------------------- 1 | class Node: 2 | def __init__(self, v = None): 3 | self.value = v 4 | self.next = None 5 | return 6 | 7 | def isempty(self): 8 | if self.value == None: 9 | return(True) 10 | else: 11 | return(False) 12 | 13 | def append(self,v): # append, recursive 14 | if self.isempty(): 15 | self.value = v 16 | elif self.next == None: 17 | newnode = Node(v) 18 | self.next = newnode 19 | else: 20 | self.next.append(v) 21 | return 22 | 23 | def insert(self,v): 24 | if self.isempty(): 25 | self.value = v 26 | return 27 | 28 | newnode = Node(v) 29 | 30 | # Evchange values in self and newnode 31 | (self.value, newnode.value) = (newnode.value, self.value) 32 | (self.next, newnode.next) = (newnode, self.next) 33 | 34 | return 35 | 36 | def delete(self,v): # delete, recursive 37 | if self.isempty(): 38 | return 39 | 40 | if self.value == v: 41 | self.value = None 42 | if self.next != None: 43 | self.value = self.next.value 44 | self.next = self.next.next 45 | return 46 | else: 47 | if self.next != None: 48 | self.next.delete(v) 49 | if self.next.value == None: 50 | self.next = None 51 | return 52 | 53 | def __str__(self): 54 | selflist = [] 55 | if self.value == None: 56 | return(str(selflist)) 57 | 58 | temp = self 59 | selflist.append(temp.value) 60 | 61 | while temp.next != None: 62 | temp = temp.next 63 | selflist.append(temp.value) 64 | 65 | return(str(selflist)) -------------------------------------------------------------------------------- /W7_L3_user_defined_lists.py: -------------------------------------------------------------------------------- 1 | # Designing our own list 2 | # a list is a sequence of nodes 3 | # Each node store value, points to next node 4 | # final node have value but points to nothing (None) 5 | # empty list have value None and points to None 6 | # singleton list have one value and it points to None 7 | 8 | class Node: 9 | 10 | def __init__(self, initial=None): 11 | self.value = initial 12 | self.next = None 13 | 14 | def isempty(self): 15 | return self.value == None 16 | 17 | def append_recursively(self, v): # append using recursion 18 | if self.isempty(): 19 | self.value = v 20 | elif self.next == None: 21 | newnode = Node(v) 22 | self.next = newnode 23 | else: 24 | self.next.append_recursively(v) 25 | return 26 | 27 | def append_iteratively(self, v): # append using iteration 28 | if self.isempty(): 29 | self.value = v 30 | return 31 | temp = self 32 | while temp.next != None: 33 | temp = temp.next 34 | newnode = Node(v) 35 | temp.next = newnode 36 | return 37 | 38 | def insert(self, v): 39 | if self.isempty(): 40 | self.value = v 41 | return 42 | newnode = Node(v) 43 | self.value, newnode.value = newnode.value, self.value 44 | self.next, newnode.next = newnode, self.next 45 | return 46 | 47 | def delete(self,v): # delete, recursive 48 | if self.isempty(): 49 | return 50 | if self.value == v: 51 | self.value = None 52 | if self.next != None: 53 | self.value = self.next.value 54 | self.next = self.next.next 55 | return 56 | else: 57 | if self.next != None: 58 | self.next.delete(v) 59 | if self.next.value == None: 60 | self.next = None 61 | return 62 | 63 | def __str__(self): # To print out the list 64 | selflist = [] 65 | if self.value == None: 66 | return(str(selflist)) 67 | 68 | temp = self 69 | selflist.append(temp.value) 70 | 71 | while temp.next != None: 72 | temp = temp.next 73 | selflist.append(temp.value) 74 | 75 | return(str(selflist)) 76 | 77 | 78 | l1 = Node() # Empty list 79 | print(l1.isempty()) 80 | # Output: True 81 | 82 | l2 = Node(5) # Singleton 83 | print(l2.isempty()) 84 | # Output: False 85 | 86 | l = Node(0) 87 | print(l) 88 | # [0] 89 | l.append_recursively(20) 90 | print(l) 91 | # [0, 20] 92 | l.append_iteratively(33) 93 | print(l) 94 | # [0, 20, 33] 95 | l.insert(100) 96 | print(l) 97 | # [100, 0, 20, 33] 98 | l.delete(20) 99 | print(l) 100 | # [100, 0, 33] -------------------------------------------------------------------------------- /W7_L4_PythonCode.py: -------------------------------------------------------------------------------- 1 | class Tree: 2 | 3 | # Empty node has self.value, self.left, self.right = None 4 | # Leaf has self.value != None, and self.left, self.right point to empty node 5 | 6 | # Constructor: create an empty node or a leaf node, depending on initval 7 | def __init__(self,initval=None): 8 | self.value = initval 9 | if self.value: 10 | self.left = Tree() 11 | self.right = Tree() 12 | else: 13 | self.left = None 14 | self.right = None 15 | return 16 | 17 | # Only empty node has value None 18 | def isempty(self): 19 | return (self.value == None) 20 | 21 | # Leaf nodes have both children empty 22 | def isleaf(self): 23 | return (self.left.isempty() and self.right.isempty()) 24 | 25 | # Convert a leaf node to an empty node 26 | def makeempty(self): 27 | self.value = None 28 | self.left = None 29 | self.right = None 30 | return 31 | 32 | # Copy right child values to current node 33 | def copyright(self): 34 | self.value = self.right.value 35 | self.left = self.right.left 36 | self.right = self.right.right 37 | return 38 | 39 | # Check if value v occurs in tree 40 | def find(self,v): 41 | if self.isempty(): 42 | return(False) 43 | 44 | if self.value == v: 45 | return(True) 46 | 47 | if v < self.value: 48 | return(self.left.find(v)) 49 | 50 | if v > self.value: 51 | return(self.right.find(v)) 52 | 53 | # Insert value v in tree 54 | def insert(self,v): 55 | if self.isempty(): 56 | self.value = v 57 | self.left = Tree() 58 | self.right = Tree() 59 | 60 | if self.value == v: 61 | return 62 | 63 | if v < self.value: 64 | self.left.insert(v) 65 | return 66 | 67 | if v > self.value: 68 | self.right.insert(v) 69 | return 70 | 71 | # Find maximum value in a nonempty tree 72 | def maxval(self): 73 | if self.right.isempty(): 74 | return(self.value) 75 | else: 76 | return(self.right.maxval()) 77 | 78 | # Delete value v from tree 79 | def delete(self,v): 80 | if self.isempty(): 81 | return 82 | 83 | if v < self.value: 84 | self.left.delete(v) 85 | return 86 | 87 | if v > self.value: 88 | self.right.delete(v) 89 | return 90 | 91 | if v == self.value: 92 | if self.isleaf(): 93 | self.makeempty() 94 | elif self.left.isempty(): 95 | self.copyright() 96 | else: 97 | self.value = self.left.maxval() 98 | self.left.delete(self.left.maxval()) 99 | return 100 | 101 | # Inorder traversal 102 | def inorder(self): 103 | if self.isempty(): 104 | return([]) 105 | else: 106 | return(self.left.inorder()+[self.value]+self.right.inorder()) 107 | 108 | # Display Tree as a string 109 | def __str__(self): 110 | return(str(self.inorder())) -------------------------------------------------------------------------------- /W7_L4_binary_search_trees.py: -------------------------------------------------------------------------------- 1 | # Dynamin sorted data 2 | # sorting is useful for efficient searching 3 | # if the data is changing dynamically: 4 | # - items are periodically inserted and deleted 5 | # - insert/delete in sorted list take time O(n) 6 | # it is inefficient to sort the data again and again, for like binary search 7 | # but in heap with priority queue we can do both efficiently 8 | # thus we can move into a tree like structure 9 | 10 | """ Binary search tree """ 11 | # for each node with value v 12 | # - value in left subtree < v 13 | # - value in right subtree > v 14 | # No duplicate values 15 | #Ex: 5 16 | # / \ 17 | # 2 8 18 | # / \ \ 19 | # 1 4 9 20 | # 2<5, 8>5,, 1<2, 4>2,, 9>8 21 | 22 | # inorder traversal : [1, 2, 4, 5, 8, 9] 23 | 24 | # Left most node in the tree have the minimum value 25 | 26 | # Right most node in the tree have the maximum value 27 | 28 | # Each node have: value, left, right 29 | # Node with 5 is like : 5, node with 2, node with 8 30 | # Node with 8 is like : 8, None, node with 9 31 | # Node with 2 is like : 2, node with 1, node with 4 32 | # Node with 1 is like : 1, None, None 33 | 34 | # leaf node have a value, have left and right, both of which point to empty node 35 | # Empty node : None, None, None 36 | # Empty tree is a single empty node 37 | # This concept makes it easier to write recursive functions to traverse the tree 38 | 39 | class Tree: 40 | 41 | # Empty node has self.value, self.left, self.right = None 42 | # Leaf has self.value != None, and self.left, self.right point to empty node 43 | 44 | # Constructor: create an empty node or a leaf node, depending on initval 45 | def __init__(self,initval=None): 46 | self.value = initval 47 | if self.value: 48 | self.left = Tree() 49 | self.right = Tree() 50 | else: 51 | self.left = None 52 | self.right = None 53 | return 54 | 55 | # Only empty node has value None 56 | def isempty(self): 57 | return (self.value == None) 58 | 59 | # Inorder traversal 60 | def inorder(self): 61 | if self.isempty(): 62 | return([]) 63 | else: 64 | return(self.left.inorder()+[self.value]+self.right.inorder()) 65 | 66 | # Display Tree as a string 67 | def __str__(self): 68 | return(str(self.inorder())) 69 | 70 | # Check if value v occurs in tree 71 | def find(self,v): 72 | if self.isempty(): 73 | return(False) 74 | 75 | if self.value == v: 76 | return(True) 77 | 78 | if v < self.value: 79 | return(self.left.find(v)) 80 | 81 | if v > self.value: 82 | return(self.right.find(v)) 83 | 84 | # Find maximum value in a nonempty tree 85 | def maxval(self): 86 | if self.right.isempty(): 87 | return(self.value) 88 | else: 89 | return(self.right.maxval()) 90 | 91 | # Find minimum value in a nonempty tree 92 | def minval(self): 93 | if self.left == None: 94 | return self.value 95 | else: 96 | return self.left.minval() 97 | 98 | # Insert value v in tree 99 | def insert(self,v): 100 | if self.isempty(): 101 | self.value = v 102 | self.left = Tree() 103 | self.right = Tree() 104 | 105 | if self.value == v: 106 | return 107 | 108 | if v < self.value: 109 | self.left.insert(v) 110 | return 111 | 112 | if v > self.value: 113 | self.right.insert(v) 114 | return 115 | 116 | # Leaf nodes have both children empty 117 | def isleaf(self): 118 | return (self.left.isempty() and self.right.isempty()) 119 | 120 | # Convert a leaf node to an empty node 121 | def makeempty(self): 122 | self.value = None 123 | self.left = None 124 | self.right = None 125 | return 126 | 127 | # Copy right child values to current node 128 | def copyright(self): 129 | self.value = self.right.value 130 | self.left = self.right.left 131 | self.right = self.right.right 132 | return 133 | 134 | # Delete value v from tree 135 | def delete(self,v): 136 | if self.isempty(): 137 | return 138 | 139 | if v < self.value: 140 | self.left.delete(v) 141 | return 142 | 143 | if v > self.value: 144 | self.right.delete(v) 145 | return 146 | 147 | if v == self.value: 148 | if self.isleaf(): 149 | self.makeempty() 150 | elif self.left.isempty(): 151 | self.copyright() 152 | else: 153 | self.value = self.left.maxval() 154 | self.left.delete(self.left.maxval()) 155 | return 156 | 157 | t = Tree() 158 | print(t) 159 | # [] 160 | t.insert(1) 161 | for i in [3, 2, 18, 7, 5, 4, 22, 14]: 162 | t.insert(i) 163 | print(t) 164 | # [1, 2, 3, 4, 5, 7, 14, 18, 22] 165 | t.insert(17) 166 | print(t) 167 | # [1, 2, 3, 4, 5, 7, 14, 17, 18, 22] 168 | t.delete(3) 169 | print(t) 170 | # [1, 2, 4, 5, 7, 14, 17, 18, 22] 171 | 172 | """ Complexity """ 173 | # all operations on search trees walk down a single path 174 | # Worst-case : height of tree 175 | # Balanced trees : height is O(log(n)) for n nodes 176 | # Trees can be balanced using rotations - look up AVL trees 177 | -------------------------------------------------------------------------------- /W7_quiz.py: -------------------------------------------------------------------------------- 1 | """ 1) Given the following permutation of a,b,c,d,e,f,g,h,i,j, 2 | what is the next permutation in lexicographic (dictionary) order? 3 | Write your answer without any blank spaces between letters. 4 | fjadbihgec """ 5 | 6 | # Ans) fjadcbeghi # using the finding next permutation 7 | 8 | """ 2) We want to add a function length() to the class Node that implements user 9 | defined lists which will compute the length of a list. An incomplete implementation 10 | of length() given below. You have to provide expressions to put in place of XXX, YYY. and ZZZ. 11 | 12 | def length(self): 13 | if self.value == None: 14 | return(XXX) 15 | elif self.next == None: 16 | return(YYY) 17 | else: 18 | return(ZZZ) 19 | 20 | XXX: 0, YYY: 0, ZZZ: self.next.length() 21 | XXX: 0, YYY: 0, ZZZ: 1 + self.next.length() 22 | XXX: 0, YYY: 1, ZZZ: self.next.length() 23 | XXX: 0, YYY: 1, ZZZ: 1 + self.next.length() """ 24 | 25 | # Ans) 4) XXX: 0, YYY: 1, ZZZ: 1 + self.next.length() 26 | 27 | """ 3) Suppose we add this function foo() to the class Tree that implements search trees. 28 | For a name mytree with a value of type Tree, what would mytree.foo() compute? 29 | 30 | def foo(self): 31 | if self.isempty(): 32 | return(0) 33 | elif self.isleaf(): 34 | return(1) 35 | else: 36 | return(1 + max(self.left.foo(),self.right.foo())) 37 | 38 | The number of nodes in mytree 39 | The largest value in mytree. 40 | The length of the longest path from root to leaf in mytree. 41 | The number of paths in mytree. """ 42 | 43 | # Ans) 3) The length of the longest path from root to leaf in mytree. 44 | 45 | """ 4) Inorder traversal of a binary tree has been defined in the lectures. 46 | A preorder traversal lists the vertices of a binary tree (not necessarily a search tree) as follows: 47 | Print the root. 48 | Print the left subtree in preorder. 49 | Print the right subtree in preorder. 50 | Suppose we have a binary tree with 10 nodes labelled a, b, c, d, e, f, g, h, i, j, with 51 | preorder traversal gbhecidajf and inorder traversal ehbicgjafd. What is the right child of the root node? """ 52 | 53 | # Ans) 'd' -------------------------------------------------------------------------------- /W8_L1_memoization_dynamic_programming.py: -------------------------------------------------------------------------------- 1 | # Inductive definitions directly gives recursive programs 2 | # fact(n-1) is a subproblem of fact(n) 3 | # so are fact(n-2), fact(n-3) etc fact(0) 4 | # isort([x2,...,xn]) is a subproblem of isort([x1,x2,...,xn]) 5 | 6 | def fib(n): 7 | if n==0 or n==1: 8 | value = n 9 | else: 10 | value = fib(n-1) + fib(n-2) 11 | return value 12 | 13 | print(fib(4)) 14 | 15 | # Here we can see that in fib(n-1) and fib(n-2) some calls 16 | # are getting repeated but done again....like, fib(2) , fib(3) etc 17 | 18 | # Thus there is an overlapping subproblems 19 | # - wasterful recomputation 20 | # - computation tree grows exponentially 21 | 22 | # we should avoid this...never re-evaluate a subproblem 23 | # build a table of values already computed 24 | # - memonry table 25 | # - memoizatioin 26 | # store each newly computed value in a table 27 | 28 | def memoized_fib(n): 29 | fibtable = {} 30 | if n in fibtable: 31 | return fibtable[n] 32 | if n==0 or n==1: 33 | value = n 34 | else: 35 | value = fib(n-1) + fib(n-2) 36 | fibtable[n] = value 37 | return value 38 | 39 | """ Dynamic programming """ 40 | # Dynamic Programming is a technique in computer programming that helps to efficiently solve 41 | # a class of problems that have overlapping subproblems and optimal substructure property. 42 | 43 | def dynamic_fib(n): 44 | fibtable = {} 45 | fibtable[0] = 0 46 | fibtable[1] = 1 47 | for i in range(2, n+1): 48 | fibtable[i] = fibtable[i-1] + fibtable[i-2] 49 | return fibtable[n] 50 | -------------------------------------------------------------------------------- /W8_L4_matrix_multiplication.py: -------------------------------------------------------------------------------- 1 | # Matrix multiplication is associative 2 | # ABC = (AB)C = A(BC) 3 | # Bracketing doesn't change answer... 4 | # but changes the complexity of computing 5 | # How? 6 | # Assume A[1,100], B[100,1], C[1,100] 7 | # computing A(BC) 8 | # - BC is [100,100], 100 x 1 x 100 = 10000 steps 9 | # - A(BC) is [1,100], 1 x 100 x 100 = 10000 steps 10 | # computing (AB)C 11 | # - AB is [1,1], 1 x 100 x 1 = 100 steps 12 | # - (AB)C is [1,100], 1 x 1 x 100 = 100 steps 13 | # A(BC) takes 20000 steps 14 | # (AB)C takes 200 steps -------------------------------------------------------------------------------- /W8_L5_Wrap_up.py: -------------------------------------------------------------------------------- 1 | # Only some difference in python compared to other 2 | # programming language -------------------------------------------------------------------------------- /W8_programming_assignment.py: -------------------------------------------------------------------------------- 1 | def solve(l1, l2): 2 | n = len(l1) 3 | 4 | if n == 0: 5 | return (0) 6 | 7 | ans = [0 for i in range(n + 1)] 8 | 9 | ans[n - 1] = max(l1[n - 1], l2[n - 1]) - min(l1[n - 1], l2[n - 1]) 10 | 11 | for i in range(n - 2, -1, -1): 12 | vert = max(l1[i], l2[i]) - min(l1[i], l2[i]) + ans[i + 1] 13 | horz = max(l1[i], l1[i + 1]) - min(l1[i], l1[i + 1]) + max(l2[i], l2[i + 1]) - min(l2[i], l2[i + 1]) + ans[ 14 | i + 2] 15 | ans[i] = max(vert, horz) 16 | 17 | return (ans[0]) 18 | 19 | 20 | nstr = input() 21 | line1str = input().strip() 22 | line1strlist = line1str.split() 23 | line1 = [] 24 | for s in line1strlist: 25 | line1.append(int(s)) 26 | 27 | line2str = input().strip() 28 | line2strlist = line2str.split() 29 | line2 = [] 30 | for s in line2strlist: 31 | line2.append(int(s)) 32 | 33 | print(solve(line1, line2)) 34 | --------------------------------------------------------------------------------