Skip to content

Commit 43bcb2a

Browse files
adding all resources
0 parents  commit 43bcb2a

File tree

85 files changed

+5777
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

85 files changed

+5777
-0
lines changed

1. Resources/BigO-cheat-sheet.pdf

28.5 KB
Binary file not shown.
Binary file not shown.
Binary file not shown.
1.05 MB
Binary file not shown.

1. Resources/DAA Syllabus.pdf

118 KB
Binary file not shown.

1. Resources/Interview cheatsheet.pdf

33.6 KB
Binary file not shown.

1. Resources/Master Plan.pdf

128 KB
Binary file not shown.

1. Resources/Master_the_Interview.pdf

26.7 KB
Binary file not shown.

2. Big-O/O(1).py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#O(1) - Constant Time
2+
#The no. of operations do not depend on the size of the input and are always constant.
3+
import time
4+
5+
array_small = ['nemo' for i in range(10)]
6+
array_medium = ['nemo' for i in range(100)]
7+
array_large = ['nemo' for i in range(10000)]
8+
9+
def finding_nemo(array):
10+
t0 = time.time()
11+
for i in array:
12+
pass
13+
t1 = time.time()
14+
print(f'Time taken = {t1-t0}')
15+
16+
finding_nemo(array_small)
17+
finding_nemo(array_medium)
18+
finding_nemo(array_large)
19+
20+
#Time taken in all 3 cases would be 0.0 seconds because we are only extracting the first and second elements of the arays.
21+
#We are not looping over the entire array.
22+
#We are performing two O(1) operations, which equal to O(2)
23+
#Any constant number can be considered as 1. There we can say this function is of O(1) - Constant Time Complexity.
24+

2. Big-O/O(m + n).py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import time
2+
3+
large1 = ['nemo' for i in range(100000)]
4+
large2 = ['nemo' for i in range(100000)]
5+
6+
def find_nemo(array1, array2):
7+
8+
#Here there are two different variables array1 and array2.
9+
#They have to be represented by 2 different variables in the Big-O representation as well.
10+
#Let array1 correspond to m and array2 correspond to n
11+
12+
t0 = time.time() #O(1)
13+
for i in range(0,len(array1)): #O(m)
14+
if array1[i] == 'nemo': #m*O(1)
15+
print("Found Nemo!!") #k1*O(1) where k1 <= m because this statement will be executed only if the if statement returns True, which can be k1(<=m) times
16+
t1 = time.time() #O(1)
17+
print(f'The search took {t1-t0} seconds.') #O(1)
18+
19+
t0 = time.time() #O(1)
20+
for i in range(0, len(array2)): #O(n)
21+
if array2[i] == 'nemo': #n*O(1)
22+
print("Found Nemo!!") #k2*O(1) where k2 <= m because this statement will be executed only if the if statement returns True, which can be k2(<=m) times
23+
t1 = time.time() #O(1)
24+
print(f'The search took {t1 - t0} seconds.') #O(1)
25+
26+
find_nemo(large1, large2)
27+
28+
#Total time complexity of the find_nemo function =
29+
#O(1 + m + m*1 + k1*1 + 1 + 1 + 1 + n + n*1 + k2*1 + 1 + 1) = O(6 + 2m + 2n + k1 + k2)
30+
#Now k1<=m and k2<=n. In the worst case, k1 can be m and k2 can be n. We'll consider the worst case and calculate the Big-O
31+
#O(6 + 2m + 2n + m + n) = O(3m + 3n + 6) = O(3(m + n + 2))
32+
#The constants can be safely ignored.
33+
#Therefore, O(m + n + 2) = O(m + n)

2. Big-O/O(m x n).py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import time
2+
3+
array1 = ['a','b','c','d','e']
4+
array2 = [1,2,3,4,5]
5+
6+
def pairs(array1, array2):
7+
8+
# Here there are two different variables array1 and array2.
9+
# They have to be represented by 2 different variables in the Big-O representation as well.
10+
# Let array1 correspond to m and array2 correspond to n
11+
12+
for i in range(len(array1)): #n*O(m)
13+
for j in range(len(array2)): #m*O(n)
14+
print(array1[i],array2[j]) #m*n*O(1)
15+
16+
pairs(array1,array2)
17+
18+
#Total time complexity of the pairs function =
19+
#O(n*m + m*n + m*n*1) = O(3*m*n)
20+
#The constants can be safely ignored.
21+
#Therefore, O(m * n * 3) = O(m * n)
22+

2. Big-O/O(n).py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import time
2+
3+
nemo = ['nemo']
4+
everyone = ['dory', 'bruce', 'marlin', 'nemo', 'gill', 'bloat', 'nigel', 'squirt', 'darla']
5+
large = ['nemo' for i in range(100000)]
6+
def find_nemo(array):
7+
t0 = time.time()
8+
for i in range(0,len(array)):
9+
if array[i] == 'nemo':
10+
print("Found Nemo!!")
11+
t1 = time.time()
12+
print(f'The search took {t1-t0} seconds.')
13+
find_nemo(nemo)
14+
find_nemo(everyone)
15+
find_nemo(large)
16+
17+
18+
def funchallenge(input):
19+
temp = 10 #O(1)
20+
temp = temp +50 #O(1)
21+
for i in range(len(input)): #O(n)
22+
var = True #n*O(1)
23+
temp += 1 #n*O(1)
24+
return temp #O(1)
25+
26+
funchallenge(nemo)
27+
funchallenge(everyone)
28+
funchallenge(large)
29+
30+
#Total running time of the funchallenge function =
31+
#O(1 + 1 + n + n*1 + n*1 + n*1 + 1) = O(3n +3) = O(3(n+1))
32+
#Any constant in the Big-O representation can be replaced by 1, as it doesn't really matter what constant it is.
33+
#Therefore, O(3(n+1)) becomes O(n+1)
34+
#Similarly, any constant number added or subtracted to n or multiplied or divided by n can also be safely written as just n
35+
#This is because the constant that operates upon n, doesn't depend on n, i.e., the input size
36+
#Therefore, the funchallenge function can be said to be of O(n) or Linear Time Complexity.

2. Big-O/O(n^2).py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import time
2+
3+
array = ['a','b','c','d','e']
4+
5+
def log_all_pairs(array):
6+
7+
#There are nested for loops in this function but there is only one variable array. So we don't need two variables for the Big-O
8+
9+
for i in range(len(array)): #n*O(n)
10+
for j in range(len(array)): #n*O(n)
11+
print(array[i], array[j]) #n*n*O(1)
12+
13+
log_all_pairs(array)
14+
15+
#Total time complexity of the log_all_pairs function =
16+
#O(n*n + n*n + n*n*1) = O(3n^2)
17+
#The constants can be safely ignored.
18+
#Therefore, O(3n^2) = O(n^2)
19+
20+
new_array = [1,2,3,4,5]
21+
def print_numbers_then_pairs(array):
22+
23+
#There are a total of three loops here but only one variable. So we need only variable for our Big-O notation
24+
25+
print("The numbers are : ") #O(1)
26+
for i in range(len(array)): #O(n)
27+
print(array[i]) #n*O(1)
28+
29+
print("The pairs are :") #O(1)
30+
for i in range(len(array)): #n*O(n)
31+
for j in range(len(array)): #n*O(n)
32+
print(array[i],array[j]) #n*n*O(1)
33+
34+
print_numbers_then_pairs(new_array)
35+
36+
#Total time complexity of the print_numbers_then_pairs function =
37+
#O(1 + n + n*1 + 1 + n*n + n*n + n*n*1) = O(3n^2 + 2n + 2)
38+
#Now, Big-O presents scalability of the cod, i.e., how the code will behave as the inputs grow larger and larger
39+
#Therefore if the expression contains terms of different degrees and the size of inputs is huge, the terms of the smaller degrees become negligible in comparison to those of the higher degrees
40+
#Therefore, we can ignore the terms of the smaller degrees and only keep the highest degree term
41+
#O(3n^2 + 2n + 2) = O(3n2)
42+
#The constants can be safely ignored.
43+
#Therefore, O(3n^2) = O(n^2)
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
#Given an array of integers, find if the array contains any duplicates.
2+
#Your function should return true if any value appears at least twice in the array, and it should return false if every element is distinct.
3+
#Example 1:
4+
#Input: [1,2,3,1]
5+
#Output: true
6+
#Example 2:
7+
#Input: [1,2,3,4]
8+
#Output: false
9+
10+
#As usual we'll get the naive approach out of the way first.
11+
12+
def brute_force_duplicate_search(array):
13+
for i in range(len(array)-1):
14+
for j in range(i+1,len(array)):
15+
if array[i] == array[j]:
16+
return True
17+
return False
18+
19+
array = [1,2,46,32,98,61,34,46]
20+
print(brute_force_duplicate_search(array))
21+
22+
#This is pretty simple, as we go through every possible pair of elements to check if they are the same.
23+
#If we find a pair having the same elements we return True, else we return False
24+
#Time Complexity - O(n^2)
25+
26+
#A slightly better solution can be :
27+
#First we sort the array using O(nlog n) built-in sort of Python.
28+
#Then we loop through the array once to check if any consecutive elements are same, which will be O(n).
29+
#So overall complexity will be O(nlog n)
30+
31+
def better_duplicate_search(array):
32+
array.sort()
33+
for i in range(len(array)-1):
34+
if array[i] == array[i+1]:
35+
return True
36+
return False
37+
38+
print(better_duplicate_search(array))
39+
40+
#An even better solution can be using a dictionary.
41+
#As we loop through the array, we'll check first if the current element is present in the dictionary
42+
#If yes, we return True
43+
#If no, we add the element to the dictionary.
44+
#Since looking up in a dictionary is O(1) time, overall complexity would be O(n)
45+
46+
def smart_duplicate_search(array):
47+
dictionary = dict()
48+
if len(array)<2:
49+
return False
50+
else:
51+
for i in range(len(array)):
52+
if array[i] in dictionary:
53+
return True
54+
else:
55+
dictionary[array[i]] = True
56+
return False
57+
58+
print(smart_duplicate_search(array))
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
#Although arrays are pre-defined in Python in the form of lists, we can implement our own arrays.
2+
#Here, we will implement our own array with some common methods such as access, push, pop, insert, delete
3+
4+
class my_array():
5+
def __init__(self):
6+
self.length = 0 #We initialize the array's length to be zero
7+
self.data = {} #We initialize the data of the array using an empty dictionary. The keys will correspond to the index and the values to the data
8+
9+
#The attributes of the array class are stored in a dictionary by default.
10+
#When the __dict__ method is called on an instance of the class it returns the attributes of the class along with their values in a dictionary format
11+
#Now, when the instance of the class is printed, it returns a class object with its location in memory.
12+
#But we know when we print the array we get the elements of the array as output
13+
#When we print the instance of the class, the built-in __str__ method is called. So we can modify the __str__ method inside the class
14+
#To suit our needs.
15+
def __str__(self):
16+
print(self.data.values())
17+
return str(self.__dict__) #This will print the attributes of the array class(length and data) in string format when print(array_instance) is executed
18+
19+
def get(self, index):
20+
return self.data[index] #This method takes in the index of the element as a parameter and returns the corresponding element in O(1) time.
21+
22+
def push(self, item):
23+
self.length += 1
24+
self.data[self.length - 1] = item #Adds the item provided to the end of the array
25+
26+
def pop(self):
27+
last_item = self.data[self.length-1] #Collects the last element
28+
del self.data[self.length - 1] #Deletes the last element from the array
29+
self.length -= 1 #Decrements the length attribute of the array by 1
30+
return last_item #Returns the popped element. O(1) time
31+
32+
def insert(self, index, item):
33+
self.length += 1
34+
for i in range(self.length-1, index, -1):
35+
self.data[i] = self.data[i-1] #Shifts every element from the index to the end by one place towards right. Thus making space at the specified index
36+
self.data[index] = item #Adds the element at the given index. O(n) operation
37+
38+
39+
def delete(self,index):
40+
for i in range(index, self.length-1):
41+
self.data[i] = self.data[i+1] #Shifts elements from the given index to the end by one place towards left
42+
del self.data[self.length - 1] #The last element which remains two times in the array is deleted
43+
self.length -= 1 #The lenght is decremented by 1. O(n) operation
44+
45+
46+
47+
arr = my_array()
48+
print(arr)
49+
print("-"*100)
50+
arr.push(6)
51+
#{'length': 1, 'data': {0: 6}}
52+
53+
arr.push(2)
54+
#{'length': 2, 'data': {0: 6, 1: 2}}
55+
56+
arr.push(9)
57+
#{'length': 3, 'data': {0: 6, 1: 2, 2: 9}}
58+
59+
arr.pop()
60+
#{'length': 2, 'data': {0: 6, 1: 2}}
61+
62+
arr.push(45)
63+
arr.push(12)
64+
arr.push(67)
65+
#{'length': 5, 'data': {0: 6, 1: 2, 2: 45, 3: 12, 4: 67}}
66+
67+
arr.insert(3,10)
68+
#{'length': 6, 'data': {0: 6, 1: 2, 2: 45, 3: 10, 4: 12, 5: 67}}
69+
70+
arr.delete(4)
71+
#{'length': 5, 'data': {0: 6, 1: 2, 2: 45, 3: 10, 4: 67}}
72+
73+
print(arr.get(1))
74+
#2
75+
76+
print(arr)
77+
#The outputs given after each function call are the outputs obtained by calling print(arr) and not by the function calls themselves
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
#Arrays are one of the most commonly-used data structures
2+
#The elements of an array are stored in contiguous memory locations
3+
#Arrays are of two types : Static and Dynamic
4+
#Static arrays have fixed, pre-defined amount of memory that they can use, whereas in dynamic arrays this is flexible
5+
#In Python we only have dynamic arrays
6+
#Some basic operations and their complexities are given below :
7+
8+
#Look-up/Accses - O(1)
9+
#Push/Pop - O(1)*
10+
#Insert - O(n)
11+
#Delete - O(n)
12+
13+
array = [5,8,2,9,17,43,25,10]
14+
15+
#Look-up/Acces
16+
#Any element of an array can be accessed by its index.
17+
#We just need to ask for the particular index of the element we are interested in and we will get the element in constant time
18+
first_element = array[0] #This will return the first element of the array, in this case, 5, in O(1) time
19+
sixth_element = array[5] #sixth-element = 43 Again, in O(1) time
20+
21+
22+
#Push/Pop
23+
#Push corresponds to pushing or adding an element at the end of the array.
24+
#Similarly, pop corresponds to removing the element at the end of the array.
25+
#Since the index of the end of the array is known, finding it and pushing or popping an element will only require O(1) time
26+
array.append(87) #Adds 87 at the end of the array in O(1) time
27+
28+
#In some special cases, the append(push) operation may take greater time. This is because as mentioned earlier, Python has dynamic arrays
29+
#So when an element is to appended and the array is filled, the entire array has to be copied to a new location
30+
#With more space allocated(generally double the space) this time so that more items can be appended.
31+
#Therefore, some individual operations may reuire O(n) time or greater, but when averaged over a large number of operations,
32+
#The complexity can be safely considered to be O(1)
33+
34+
array.pop() #Pops/removes the element at the end of the array in O(1) time.
35+
36+
print(array)
37+
38+
39+
#Insert
40+
#Insert operation inserts an element at the beginning of the array, or at any location specified.
41+
#This is O(n) operation since after inserting the element at the desired location,
42+
#The elements to the right of the array have to be updated with the correct index as they all have shifted by one place.
43+
#This requires looping through the array. Hence, O(n) time.
44+
array.insert(0,50) #Inserts 50 at the beginning of the array and shifts all other elements one place towards right. O(n)
45+
array.insert(4,0) #inserts '0' at index '4', thus shifting all elements starting from index 4 one place towards right. O(n)
46+
47+
print(array)
48+
49+
50+
#Delete
51+
#Similar to insert, it deletes an element from the specified location in O(n) time
52+
#The elements to the right of the deleted element have to shifted one space left, which requires looping over the entire array
53+
#Hence, O(n) time complexity
54+
array.pop(0) #This pops the first element of the array, shifting the remaining elements of the array one place left. O(n)
55+
print(array)
56+
array.remove(17) #This command removes the first occurence of the element 17 in the array, for which it needs to traverse the entire array, which requires O(n) time
57+
print(array)
58+
del array[2:4] #This command deletes elements from position 2 to position 4, again, in O(n) time
59+
print(array)
60+
61+
array.insert(11, 'shushrut')
62+
print(array)
63+
print("-"*100)
64+
65+
l = [5,8,2,9,17,43,25,10]
66+
print(len)
67+
l.insert(9, "shushrut")
68+
len = len(l)
69+
print(len)
70+
71+
72+
a = 15
73+
b = 15
74+
c = 300
75+
d = 300
76+
print(a is b, c is d)

0 commit comments

Comments
 (0)