diff --git a/Exercise_1.py b/Exercise_1.py index 3e6adcf4..6b949bf6 100644 --- a/Exercise_1.py +++ b/Exercise_1.py @@ -1,3 +1,14 @@ +# Time Complexity : O(log(base 2)(N)) +# Space Complexity : O(1) +# Did this code successfully run on Leetcode : Could not find the problem on leetcode +# Any problem you faced while coding this : No + + +# Your code here along with comments explaining your approach +# We can use Binary Search when the array is sorted. It reduces the search time of O(N) i.e. linear to O(log(base 2)(N)) i.e. logarithmic +# I am assuming the input array on which we are applying the Binary Search would always be sorted +# Reducing the search space by half on every iteration + # Python code to implement iterative Binary # Search. @@ -6,6 +17,20 @@ def binarySearch(arr, l, r, x): #write your code here + if((len(arr) == 0) or (not (0 <= l <= len(arr)-1)) or (not (0 <= r <= len(arr)-1)) or (x < arr[0]) or (x > arr[-1])): + return -1 + + while(l <= r): + mid = ((2 * r) - (r - l)) // 2 + if(x == arr[mid]): + return mid + elif(x < arr[mid]): + r = mid - 1 + else: + l = mid + 1 + + return -1 + @@ -14,9 +39,45 @@ def binarySearch(arr, l, r, x): x = 10 # Function call -result = binarySearch(arr, 0, len(arr)-1, x) - +result = binarySearch(arr, 0, len(arr)-1, x) +if result != -1: + print("Element is present at index %d" % result) +else: + print("Element is not present in array") + +result = binarySearch(arr, 0, len(arr)-1, 0) +if result != -1: + print("Element is present at index %d" % result) +else: + print("Element is not present in array") + +result = binarySearch(arr, 0, len(arr)-1, 50) +if result != -1: + print("Element is present at index %d" % result) +else: + print("Element is not present in array") + +result = binarySearch(arr, 0, len(arr)-1, 6) +if result != -1: + print("Element is present at index %d" % result) +else: + print("Element is not present in array") + +result = binarySearch(arr, 0, len(arr)-1, 2) +if result != -1: + print("Element is present at index %d" % result) +else: + print("Element is not present in array") + +result = binarySearch(arr, 0, -1, 2) +if result != -1: + print("Element is present at index %d" % result) +else: + print("Element is not present in array") + +result = binarySearch(arr, len(arr), 3, 2) if result != -1: - print "Element is present at index % d" % result + print("Element is present at index %d" % result) else: - print "Element is not present in array" + print("Element is not present in array") + diff --git a/Exercise_2.py b/Exercise_2.py index 35abf0dd..dca636c6 100644 --- a/Exercise_2.py +++ b/Exercise_2.py @@ -1,23 +1,90 @@ +# Time Complexity : Best and Average case: O(N*LogN) when partition happens approx from the middle that is position of pivot +# element is mostly near the middle of the array. +# Worst Case: O(N**2) = O(N^2): When array is already sorted in Ascending or Descending Order. In this case partitions are very +# imbalanced. The number of partitions would be O(N). Also our partition function is already O(N). So overall Time complexity would be O(N**2) +# Space Complexity : O(1) extra space. O(N) auxiliary recursive stack space +# Did this code successfully run on Leetcode : Could not find on Leetcode +# Any problem you faced while coding this : I went through Algorithm explanation and psuedo code. Then I was able to implement it. +# There are many ways to implement the partition function. Can you please guide which one is better? + + +# Your code here along with comments explaining your approach +# Here I have written the code for Sorting in Ascending Order. +# It is based on choosing pivot and then partitioning the array +# Here I am taking pivot as the last element that is arr[high] for the range of [low, high] +# Then we place the pivot on its correct place by bringing all smaller and equal to pivot elements on the Left Portion and larger than pivot +# as the right portion +# Then we recursively apply the pivot and partition logic on the Left portion and the Right Portion +# If we want to sort the array in descending order, I believe with minor tweak in the partition function "if(arr[j] >= pivot)" , +# would help us to do it. It would bring the larger and equal to pivot elements to the left + # Python program for implementation of Quicksort Sort # give you explanation for the approach def partition(arr,low,high): - - #write your code here + pivot = arr[high] + # used to bring the smaller or equal to pivot elements to the left portion + idx = low - 1 + + for j in range(low, high): + if(arr[j] <= pivot): + idx += 1 + arr[j], arr[idx] = arr[idx], arr[j] + + # bring the pivot to its correct place + idx += 1 + arr[high], arr[idx] = arr[idx], arr[high] + return idx + # Function to do Quick sort -def quickSort(arr,low,high): - +def quickSort(arr,low,high): #write your code here - + if(low < high): + pIndex = partition(arr,low,high) + quickSort(arr, low, pIndex - 1) + quickSort(arr, pIndex + 1, high) + # Driver code to test above -arr = [10, 7, 8, 9, 1, 5] +arr = [10, 7, 8, 9, 1, 5] +n = len(arr) +quickSort(arr,0,n-1) +print("Sorted array is:") +for i in range(n): + print("%d " %arr[i], end="") + +print() +arr = [6, 5, 4, 3, 2, 1] +n = len(arr) +quickSort(arr,0,n-1) +print("Sorted array is:") +for i in range(n): + print("%d " %arr[i], end="") + +print() +arr = [7, 8, 9, 10, 11, 12, 13] +n = len(arr) +quickSort(arr,0,n-1) +print("Sorted array is:") +for i in range(n): + print("%d " %arr[i], end="") + +print() +arr = [7, 7, 7, 7, 7, 7] +n = len(arr) +quickSort(arr,0,n-1) +print("Sorted array is:") +for i in range(n): + print("%d " %arr[i], end="") + +print() +arr = [] n = len(arr) quickSort(arr,0,n-1) -print ("Sorted array is:") +print("Sorted array is:") for i in range(n): - print ("%d" %arr[i]), + print("%d " %arr[i], end="") diff --git a/Exercise_3.py b/Exercise_3.py index a26a69b8..75c99925 100644 --- a/Exercise_3.py +++ b/Exercise_3.py @@ -1,26 +1,73 @@ +# Time Complexity : O(len(LinkedList) / 2) = O(N / 2) - Taken for printMiddle() function +# Space Complexity : O(len(LinkedList)) = O(N) = Non-Contiguous space of O(N). Each node stores data and reference to the next node. +# Did this code successfully run on Leetcode : Yes +# Any problem you faced while coding this : No + + +# Your code here along with comments explaining your approach +# If length of LinkedList is Odd, then we have only one middle +# If length of LinkedList is Even, then we have two middle elements. Here I am returning the First Middle. +# Example if LinkedList is [1,2,3,4] then my first middle would be 2 +# Here I am using fast and slow pointer approach. Our fast pointer travels at a speed of 2 times the speed of the slow pointer. +# So when my fast pointer has covered a distance of 2*x, our slow pointer reaches a distance of x. Using this analogy, when +# our fast pointer reaches the last node, our slow pointer has reached the middle of the LinkedList + + # Node class class Node: # Function to initialise the node object - def __init__(self, data): + def __init__(self, data): + self.data = data + self.next = None class LinkedList: - def __init__(self): + def __init__(self): + self.head = None + self.tail = None - def push(self, new_data): + def push(self, new_data): + newNode = Node(new_data) + if(not self.head): + self.head = newNode + self.tail = newNode + else: + self.tail.next = newNode + self.tail = newNode + # Function to get the middle of # the linked list - def printMiddle(self): + def printMiddle(self): + if(not self.head): + print("LinkedList is Empty") + return + + slow = self.head + fast = self.head + + while(fast.next and fast.next.next): + fast = fast.next.next + slow = slow.next + + print(slow.data) + + + # Driver code -list1 = LinkedList() -list1.push(5) -list1.push(4) +list1 = LinkedList() +list1.printMiddle() +list1.push(5) +list1.printMiddle() +list1.push(4) +list1.printMiddle() list1.push(2) list1.push(3) list1.push(1) -list1.printMiddle() +list1.printMiddle() +list1.push(20) +list1.printMiddle() diff --git a/Exercise_4.py b/Exercise_4.py index 9bc25d3d..fdf3139e 100644 --- a/Exercise_4.py +++ b/Exercise_4.py @@ -1,12 +1,65 @@ +# Time Complexity : O(N * (Log(Base 2) N)) -- O(Log(Base 2) N) is for the steps taken to dividing the array into 2 parts on each step. +# O(N) is for the merging step +# Space Complexity : O(N) -- creating a temp array when merging the left and right parts +# Did this code successfully run on Leetcode : Could not find it on Leetcode +# Any problem you faced while coding this : I went through Algorithm explanation and psuedo code and then was able to implement it + + +# Your code here along with comments explaining your approach +# Here I have written the code for Sorting in Ascending Order. I believe with minor tweak in the merge routine we can Sort the array in +# Descending Order as well. Keep dividing the array into half length each until we reach 1 element. Then when we merge, we compare +# the elements of two divided arrays and place the elements in a sorted way in the new array and then merge it with the actual array + # Python program for implementation of MergeSort def mergeSort(arr): #write your code here + def mergeSortRecur(arr, low, high): + # base case to stop the recursion + if(low >= high): + return + + mid = (high + low) // 2 + # divide into left part + mergeSortRecur(arr, low, mid) + # divide into right part + mergeSortRecur(arr, mid + 1, high) + + # merge routine + p1 = low + p2 = mid + 1 + temp = [] + while((p1 <= mid) and (p2 <= high)): + if(arr[p1] <= arr[p2]): + temp.append(arr[p1]) + p1 += 1 + else: + temp.append(arr[p2]) + p2 += 1 + + while(p1 <= mid): + temp.append(arr[p1]) + p1 += 1 + + while(p2 <= high): + temp.append(arr[p2]) + p2 += 1 + + # update the actual array + k = low + for i in range(len(temp)): + arr[k] = temp[i] + k += 1 + + if((len(arr) == 0) or (len(arr) == 1)): + return + mergeSortRecur(arr, 0, len(arr) - 1) # Code to print the list -def printList(arr): +def printList(arr): - #write your code here + #write your code here + print(arr) # driver code to test the above code if __name__ == '__main__': @@ -15,4 +68,18 @@ def printList(arr): printList(arr) mergeSort(arr) print("Sorted array is: ", end="\n") + printList(arr) + print("-----------------") + arr = [12] + print ("Given array is", end="\n") + printList(arr) + mergeSort(arr) + print("Sorted array is: ", end="\n") + printList(arr) + print("-----------------") + arr = [] + print ("Given array is", end="\n") printList(arr) + mergeSort(arr) + print("Sorted array is: ", end="\n") + printList(arr) diff --git a/Exercise_5.py b/Exercise_5.py index 1da24ffb..738f783e 100644 --- a/Exercise_5.py +++ b/Exercise_5.py @@ -1,10 +1,87 @@ +# Time Complexity : Best and Average case: O(N*LogN) when partition happens approx from the middle that is position of pivot +# element is mostly at the middle of the array. +# Worst Case: O(N**2) = O(N^2): When array is already sorted in Ascending or Descending Order. In this case partitions are very +# imbalanced. The number of partitions would be O(N). Also our partition function is already O(N). So overall Time complexity would be O(N**2) +# Space Complexity : O(N) for stack +# Did this code successfully run on Leetcode : Could not find on Leetcode +# Any problem you faced while coding this : Had to check which data structure we could use to eliminate recursion. Then realised it +# could easily be replaced by stack. Then I was able to implement it + + +# Your code here along with comments explaining your approach +# Approach is similar to quick sort as done in recursive. Just used stack to eliminate recursion. Stack would be storing my left portion +# and right portion indices. + # Python program for implementation of Quicksort # This function is same in both iterative and recursive def partition(arr, l, h): #write your code here + pivot = arr[h] + idx = l - 1 + for j in range(l, h): + if(arr[j] <= pivot): + idx += 1 + arr[j], arr[idx] = arr[idx], arr[j] + + idx += 1 + arr[h], arr[idx] = arr[idx], arr[h] + return idx def quickSortIterative(arr, l, h): #write your code here + if(l < h): + stack = [(l, h)] + while(stack): + low, high = stack.pop() + pIndex = partition(arr, low, high) + + if(pIndex - 1 > low): + stack.append((low, pIndex - 1)) + + if(pIndex + 1 < high): + stack.append((pIndex + 1, high)) + + + + +# Driver code to test above +arr = [10, 7, 8, 9, 1, 5] +n = len(arr) +quickSortIterative(arr,0,n-1) +print("Sorted array is:") +for i in range(n): + print("%d " %arr[i], end="") + +print() +arr = [6, 5, 4, 3, 2, 1] +n = len(arr) +quickSortIterative(arr,0,n-1) +print("Sorted array is:") +for i in range(n): + print("%d " %arr[i], end="") + +print() +arr = [7, 8, 9, 10, 11, 12, 13] +n = len(arr) +quickSortIterative(arr,0,n-1) +print("Sorted array is:") +for i in range(n): + print("%d " %arr[i], end="") + +print() +arr = [7, 7, 7, 7, 7, 7] +n = len(arr) +quickSortIterative(arr,0,n-1) +print("Sorted array is:") +for i in range(n): + print("%d " %arr[i], end="") +print() +arr = [] +n = len(arr) +quickSortIterative(arr,0,n-1) +print("Sorted array is:") +for i in range(n): + print("%d " %arr[i], end="") \ No newline at end of file