DSA Interview Qs
DSA Interview Qs
Lists ...................................................................................................................................... 3
Example: Python List Operations ........................................................................................ 3
Tuple ..................................................................................................................................... 5
Example: Python Tuple Operations...................................................................................... 5
Set ........................................................................................................................................ 6
Example: Python Set Operations ......................................................................................... 8
Frozen Sets ............................................................................................................................ 9
String................................................................................................................................... 10
Dictionary ............................................................................................................................ 11
Matrix .................................................................................................................................. 12
Bytearray ............................................................................................................................. 13
Linked List ........................................................................................................................... 14
Linked List Traversal............................................................................................................. 18
Stack ................................................................................................................................... 19
Queue ................................................................................................................................. 21
Priority Queue .................................................................................................................. 23
Heap ................................................................................................................................... 25
Binary Tree........................................................................................................................... 27
Tree Traversal ...................................................................................................................... 27
Depth First Traversals: ...................................................................................................... 28
Algorithm Inorder(tree) ..................................................................................................... 28
Algorithm Preorder(tree) ................................................................................................... 28
Algorithm Postorder(tree) ................................................................................................. 28
Breadth-First or Level Order Traversal ............................................................................... 28
Binary Search Tree ............................................................................................................... 29
Graphs ................................................................................................................................ 30
Adjacency Matrix .............................................................................................................. 30
Adjacency List .................................................................................................................. 31
Graph Traversal ................................................................................................................... 31
Breadth-First Search or BFS .............................................................................................. 31
Depth First Search or DFS ................................................................................................. 31
Recursion ............................................................................................................................ 32
What is the base condition in recursion? ........................................................................... 32
How memory is allocated to different function calls in recursion? ...................................... 32
Dynamic Programming ......................................................................................................... 33
Tabulation vs Memoization ............................................................................................... 33
Tabulation .................................................................................................................... 34
Memoization ................................................................................................................. 34
Searching Algorithms ........................................................................................................... 34
Linear Search ................................................................................................................... 34
Binary Search ................................................................................................................... 36
Sorting Algorithms ............................................................................................................... 38
Selection Sort................................................................................................................... 38
Bubble Sort ...................................................................................................................... 39
Insertion Sort ................................................................................................................... 41
Merge Sort........................................................................................................................ 43
QuickSort ......................................................................................................................... 47
Partition Algorithm ............................................................................................................... 48
Lists
Python Lists are ordered collections of data just like arrays in other programming languages. It
allows different types of elements in the list. The implementation of Python List is similar to
Vectors in C++ or ArrayList in JAVA. The costly operation is inserting or deleting the element
from the beginning of the List as all the elements are needed to be shifted. Insertion and
deletion at the end of the list can also become costly in the case where the preallocated
memory becomes full.
Python3
print(List)
Output
List elements can be accessed by the assigned index. In python starting index of the list, a
sequence is 0 and the ending index is (if N elements are there) N-1.
print(List)
print(List2)
print(List[0])
print(List[2])
# negative indexing
print(List[-1])
print(List[-3])
Output
Multi-Dimensional List:
Geeks
Geeks
Geeks
Tuple
Python tuples are similar to lists but Tuples are immutable in nature i.e. once created it cannot
be modified. Just like a List, a Tuple can also contain elements of various types.
In Python, tuples are created by placing a sequence of values separated by ‘comma’ with or
without the use of parentheses for grouping of the data sequence.
Note: To create a tuple of one element there must be a trailing comma. For example, (8,) will
create a tuple containing 8 as the element.
print(Tuple)
list1 = [1, 2, 4, 5, 6]
Tuple = tuple(list1)
print(Tuple[0])
# negative indexing
print(Tuple[-1])
print("\nThird last element of tuple")
print(Tuple[-3])
Output
('Geeks', 'For')
Set
Python set is a mutable collection of data that does not allow any duplication. Sets are
basically used to include membership testing and eliminating duplicate entries. The data
structure used in this is Hashing, a popular technique to perform insertion, deletion, and
traversal in O(1) on average.
If Multiple values are present at the same index position, then the value is appended to that
index position, to form a Linked List. In, CPython Sets are implemented using a dictionary with
dummy variables, where key beings the members set with greater optimizations to the time
complexity.
Set Implementation:
Sets with Numerous operations on a single HashTable:
Example: Python Set Operations
Python3
print(Set)
# for loop
for i in Set:
print()
# using in keyword
print("Geeks" in Set)
Output
Elements of set:
1 2 4 6 For Geeks
True
Frozen Sets
Frozen sets in Python are immutable objects that only support methods and operators that
produce a result without affecting the frozen set or sets to which they are applied. While
elements of a set can be modified at any time, elements of the frozen set remain the same after
creation.
Python3
print("Normal Set")
print(normal_set)
# A frozen set
print("\nFrozen Set")
print(frozen_set)
# frozen_set.add("h")
Output
Normal Set
Frozen Set
String
Python Strings is the immutable array of bytes representing Unicode characters. Python does
not have a character data type, a single character is simply a string with a length of 1.
Note: As strings are immutable, modifying a string will result in creating a new copy.
Python3
print(String)
print(String[0])
Output
Creating String:
Welcome to GeeksForGeeks
Dictionary
Python dictionary is an unordered collection of data that stores data in the format of key:value
pair. It is like hash tables in any other language with the time complexity of O(1). Indexing of
Python Dictionary is done with the help of keys. These are of any hashable type i.e. an object
whose can never change like strings, numbers, tuples, etc. We can create a dictionary by using
curly braces ({}) or dictionary comprehension.
Python3
# Creating a Dictionary
print(Dict)
print(Dict['Name'])
# method
print(Dict.get(1))
# creation using Dictionary comprehension
print(myDict)
Output
Creating Dictionary:
Geeks
[1, 2, 3, 4]
Matrix
A matrix is a 2D array where each element is of strictly the same size. To create a matrix we will
be using the NumPy package.
Python3
import numpy as np
a = np.array([[1,2,3,4],[4,55,1,2],
[8,3,20,19],[11,2,22,21]])
m = np.reshape(a,(4, 4))
print(m)
# Accessing element
print("\nAccessing Elements")
print(a[1])
print(a[2][0])
# Adding Element
m = np.append(m,[[1, 15,13,11]],0)
print("\nAdding Element")
print(m)
# Deleting Element
m = np.delete(m,[1],0)
print("\nDeleting Element")
print(m)
Output
[[ 1 2 3 4]
[ 4 55 1 2]
[ 8 3 20 19]
[11 2 22 21]]
Accessing Elements
[ 4 55 1 2]
8
Adding Element
[[ 1 2 3 4]
[ 4 55 1 2]
[ 8 3 20 19]
[11 2 22 21]
[ 1 15 13 11]]
Deleting Element
[[ 1 2 3 4]
[ 8 3 20 19]
[11 2 22 21]
[ 1 15 13 11]]
Bytearray
Python Bytearray gives a mutable sequence of integers in the range 0 <= x < 256.
Python3
# Creating bytearray
print("Creating Bytearray:")
print(a)
# accessing elements
# modifying elements
a[1] = 3
print("\nAfter Modifying:")
print(a)
# Appending elements
a.append(30)
print(a)
Output
Creating Bytearray:
bytearray(b'\x0c\x08\x19\x02')
Accessing Elements: 8
After Modifying:
bytearray(b'\x0c\x03\x19\x02')
bytearray(b'\x0c\x03\x19\x02\x1e')
Linked List
A linked list is a linear data structure, in which the elements are not stored at contiguous
memory locations. The elements in a linked list are linked using pointers as shown in the below
image:
A linked list is represented by a pointer to the first node of the linked list. The first node is called
the head. If the linked list is empty, then the value of the head is NULL. Each node in a list
consists of at least two parts:
• Data
Python3
# Node class
class Node:
# next as null
class LinkedList:
# List object
def __init__(self):
self.head = None
Python3
class Node:
class LinkedList:
def __init__(self):
self.head = None
if __name__=='__main__':
llist = LinkedList()
llist.head = Node(1)
second = Node(2)
third = Node(3)
'''
| | |
| | |
'''
'''
| | |
| | |
'''
'''
| | |
| | |
'''
Python3
# Node class
class Node:
class LinkedList:
def __init__(self):
self.head = None
def printList(self):
temp = self.head
while (temp):
print (temp.data)
temp = temp.next
if __name__=='__main__':
llist = LinkedList()
llist.head = Node(1)
second = Node(2)
third = Node(3)
llist.printList()
Output
Stack
A stack is a linear data structure that stores items in a Last-In/First-Out (LIFO) or First-In/Last-
Out (FILO) manner. In stack, a new element is added at one end and an element is removed
from that end only. The insert and delete operations are often called push and pop.
The functions associated with stack are:
• top() – Returns a reference to the topmost element of the stack – Time Complexity: O(1)
• push(a) – Inserts the element ‘a’ at the top of the stack – Time Complexity: O(1)
• pop() – Deletes the topmost element of the stack – Time Complexity: O(1)
Python3
stack = []
stack.append('g')
stack.append('f')
stack.append('g')
print('Initial stack')
print(stack)
# LIFO order
print(stack.pop())
print(stack.pop())
print(stack.pop())
print(stack)
# uncommenting print(stack.pop())
Output
Initial stack
[]
Queue
As a stack, the queue is a linear data structure that stores items in a First In First Out (FIFO)
manner. With a queue, the least recently added item is removed first. A good example of the
queue is any queue of consumers for a resource where the consumer that came first is served
first.
Operations associated with queue are:
• Enqueue: Adds an item to the queue. If the queue is full, then it is said to be an Overflow
condition – Time Complexity: O(1)
• Dequeue: Removes an item from the queue. The items are popped in the same order in
which they are pushed. If the queue is empty, then it is said to be an Underflow
condition – Time Complexity: O(1)
• Front: Get the front item from queue – Time Complexity: O(1)
• Rear: Get the last item from queue – Time Complexity: O(1)
Python3
# Initializing a queue
queue = []
queue.append('g')
queue.append('f')
queue.append('g')
print("Initial queue")
print(queue)
print(queue.pop(0))
print(queue.pop(0))
print(queue.pop(0))
print(queue)
# Uncommenting print(queue.pop(0))
Output
Initial queue
[]
Priority Queue
Priority Queues are abstract data structures where each data/value in the queue has a certain
priority. For example, In airlines, baggage with the title “Business” or “First-class” arrives earlier
than the rest. Priority Queue is an extension of the queue with the following properties.
• An element with high priority is dequeued before an element with low priority.
• If two elements have the same priority, they are served according to their order in the
queue.
Python3
# using Queue.
class PriorityQueue(object):
def __init__(self):
self.queue = []
def __str__(self):
def isEmpty(self):
return len(self.queue) == 0
self.queue.append(data)
def delete(self):
try:
max = 0
for i in range(len(self.queue)):
max = i
item = self.queue[max]
del self.queue[max]
return item
except IndexError:
print()
exit()
if __name__ == '__main__':
myQueue = PriorityQueue()
myQueue.insert(12)
myQueue.insert(1)
myQueue.insert(14)
myQueue.insert(7)
print(myQueue)
print(myQueue.delete())
Output
12 1 14 7
14
12
Heap
heapq module in Python provides the heap data structure that is mainly used to represent a
priority queue. The property of this data structure is that it always gives the smallest element
(min heap) whenever the element is popped. Whenever elements are pushed or popped, heap
structure is maintained. The heap[0] element also returns the smallest element each time. It
supports the extraction and insertion of the smallest element in the O(log n) times.
• Max-Heap: In a Max-Heap the key present at the root node must be greatest among the
keys present at all of it’s children. The same property must be recursively true for all
sub-trees in that Binary Tree.
• Min-Heap: In a Min-Heap the key present at the root node must be minimum among the
keys present at all of it’s children. The same property must be recursively true for all
sub-trees in that Binary Tree.
Python3
import heapq
# initializing list
li = [5, 7, 9, 1, 3]
heapq.heapify(li)
print (list(li))
# pushes 4
heapq.heappush(li,4)
# printing modified heap
print (list(li))
print (heapq.heappop(li))
Output
Binary Tree
A tree is a hierarchical data structure that looks like the below figure –
tree
----
j <-- root
/ \
f k
/ \ \
a h z <-- leaves
The topmost node of the tree is called the root whereas the bottommost nodes or the nodes
with no children are called the leaf nodes. The nodes that are directly under a node are called
its children and the nodes that are directly above something are called its parent.
A binary tree is a tree whose elements can have almost two children. Since each element in a
binary tree can have only 2 children, we typically name them the left and right children. A Binary
Tree node contains the following parts.
• Data
Tree Traversal
Trees can be traversed in different ways. Following are the generally used ways for traversing
trees. Let us consider the below tree –
tree
----
1 <-- root
/ \
2 3
/\
4 5
Algorithm Inorder(tree)
• Traverse the left subtree, i.e., call Inorder(left-subtree)
Algorithm Preorder(tree)
• Visit the root.
Algorithm Postorder(tree)
• Traverse the left subtree, i.e., call Postorder(left-subtree)
For each node, first, the node is visited and then its child nodes are put in a FIFO queue. Below
is the algorithm for the same –
• print temp_node->data.
• The left subtree of a node contains only nodes with keys lesser than the node’s key.
• The right subtree of a node contains only nodes with keys greater than the node’s key.
• The left and right subtree each must also be a binary search tree.
The above properties of the Binary Search Tree provide an ordering among keys so that the
operations like search, minimum and maximum can be done fast. If there is no order, then we
may have to compare every key to search for a given key.
Searching Element
• Compare the searching element with root, if less than root, then recurse for left, else
recurse for right.
• If the element to search is found anywhere, return true, else return false.
Python3
def search(root,key):
return root
# Key is greater than root's key
return search(root.right,key)
return search(root.left,key)
Insertion of a key
• Compare the inserting element with root, if less than root, then recurse for left, else
recurse for right.
• After reaching the end, just insert that node at left(if less than current) else right.
Graphs
A graph is a nonlinear data structure consisting of nodes and edges. The nodes are sometimes
also referred to as vertices and the edges are lines or arcs that connect any two nodes in the
graph. More formally a Graph can be defined as a Graph consisting of a finite set of vertices(or
nodes) and a set of edges that connect a pair of nodes.
In the above Graph, the set of vertices V = {0,1,2,3,4} and the set of edges E = {01, 12, 23, 34, 04,
14, 13}. The following two are the most commonly used representations of a graph.
• Adjacency Matrix
• Adjacency List
Adjacency Matrix
Adjacency Matrix is a 2D array of size V x V where V is the number of vertices in a graph. Let the
2D array be adj[][], a slot adj[i][j] = 1 indicates that there is an edge from vertex i to vertex j. The
adjacency matrix for an undirected graph is always symmetric. Adjacency Matrix is also used to
represent weighted graphs. If adj[i][j] = w, then there is an edge from vertex i to vertex j with
weight w.
Adjacency List
An array of lists is used. The size of the array is equal to the number of vertices. Let the array be
an array[]. An entry array[i] represents the list of vertices adjacent to the ith vertex. This
representation can also be used to represent a weighted graph. The weights of edges can be
represented as lists of pairs. Following is the adjacency list representation of the above graph.
Graph Traversal
Breadth-First Search or BFS
Breadth-First Traversal for a graph is similar to Breadth-First Traversal of a tree. The only catch
here is, unlike trees, graphs may contain cycles, so we may come to the same node again. To
avoid processing a node more than once, we use a boolean visited array. For simplicity, it is
assumed that all vertices are reachable from the starting vertex.
For example, in the following graph, we start traversal from vertex 2. When we come to vertex 0,
we look for all adjacent vertices of it. 2 is also an adjacent vertex of 0. If we don’t mark visited
vertices, then 2 will be processed again and it will become a non-terminating process. A
Breadth-First Traversal of the following graph is 2, 0, 3, 1.
Algorithm:
• Create a recursive function that takes the index of the node and a visited array.
• Traverse all the adjacent and unmarked nodes and call the recursive function with the
index of the adjacent node.
Recursion
The process in which a function calls itself directly or indirectly is called recursion and the
corresponding function is called a recursive function. Using the recursive algorithms, certain
problems can be solved quite easily. Examples of such problems are Towers of Hanoi (TOH),
Inorder/Preorder/Postorder Tree Traversals, DFS of Graph, etc.
def fact(n):
# base case
if (n < = 1)
return 1
else
return n*fact(n-1)
In the above example, base case for n < = 1 is defined and larger value of number can be solved
by converting to smaller one till base case is reached.
Let us take the example of how recursion works by taking a simple function.
Python3
# A Python 3 program to
# demonstrate working of
# recursion
def printFun(test):
return
else:
return
# Driver Code
test = 3
printFun(test)
Output
321123
>>> More
Dynamic Programming
Dynamic Programming is mainly an optimization over plain recursion. Wherever we see a
recursive solution that has repeated calls for same inputs, we can optimize it using Dynamic
Programming. The idea is to simply store the results of subproblems, so that we do not have to
re-compute them when needed later. This simple optimization reduces time complexities from
exponential to polynomial. For example, if we write simple recursive solution for Fibonacci
Numbers, we get exponential time complexity and if we optimize it by storing solutions of
subproblems, time complexity reduces to linear.
Tabulation vs Memoization
There are two different ways to store the values so that the values of a sub-problem can be
reused. Here, will discuss two patterns of solving dynamic programming (DP) problem:
• Tabulation: Bottom Up
Tabulation
As the name itself suggests starting from the bottom and accumulating answers to the top.
Let’s discuss in terms of state transition.
Let’s describe a state for our DP problem to be dp[x] with dp[0] as base state and dp[n] as our
destination state. So, we need to find the value of destination state i.e dp[n].
If we start our transition from our base state i.e dp[0] and follow our state transition relation to
reach our destination state dp[n], we call it the Bottom-Up approach as it is quite clear that we
started our transition from the bottom base state and reached the topmost desired state.
To know this let’s first write some code to calculate the factorial of a number using bottom up
approach. Once, again as our general procedure to solve a DP we first define a state. In this
case, we define a state as dp[x], where dp[x] is to find the factorial of x.
Memoization
Once, again let’s describe it in terms of state transition. If we need to find the value for some
state say dp[n] and instead of starting from the base state that i.e dp[0] we ask our answer from
the states that can reach the destination state dp[n] following the state transition relation, then
it is the top-down fashion of DP.
Here, we start our journey from the top most destination state and compute its answer by
taking in count the values of states that can reach the destination state, till we reach the
bottom-most base state.
Searching Algorithms
Linear Search
• Start from the leftmost element of arr[] and one by one compare x with each element of
arr[]
# otherwise return -1
if (arr[i] == x):
return i
return -1
# Driver Code
x = 10
n = len(arr)
# Function call
result = search(arr, n, x)
if(result == -1):
Output
Binary Search
Search a sorted array by repeatedly dividing the search interval in half. Begin with an interval
covering the whole array. If the value of the search key is less than the item in the middle of the
interval, narrow the interval to the lower half. Otherwise, narrow it to the upper half. Repeatedly
check until the value is found or the interval is empty.
Python3
if r >= l:
mid = l + (r - l) // 2
if arr[mid] == x:
return mid
# in right subarray
else:
else:
return -1
# Driver Code
arr = [ 2, 3, 4, 10, 40 ]
x = 10
# Function call
if result != -1:
else:
print ("Element is not present in array")
Output
Sorting Algorithms
Selection Sort
The selection sort algorithm sorts an array by repeatedly finding the minimum element
(considering ascending order) from unsorted part and putting it at the beginning. In every
iteration of selection sort, the minimum element (considering ascending order) from the
unsorted subarray is picked and moved to the sorted subarray.
Python3
# Sort
import sys
for i in range(len(A)):
# unsorted array
min_idx = i
min_idx = j
for i in range(len(A)):
print("%d" %A[i]),
Output
Sorted array
11
12
22
25
64
Bubble Sort
Bubble Sort is the simplest sorting algorithm that works by repeatedly swapping the adjacent
elements if they are in wrong order.
Illustration :
Python3
def bubbleSort(arr):
n = len(arr)
for i in range(n):
bubbleSort(arr)
for i in range(len(arr)):
Output
11
12
22
25
34
64
90
Insertion Sort
To sort an array of size n in ascending order using insertion sort:
• If the key element is smaller than its predecessor, compare it to the elements before.
Move the greater elements one position up to make space for the swapped element.
Illustration:
Python3
def insertionSort(arr):
key = arr[i]
# Move elements of arr[0..i-1], that are
j = i-1
arr[j + 1] = arr[j]
j -= 1
arr[j + 1] = key
insertionSort(arr)
for i in range(len(arr)):
Output
11
12
13
Merge Sort
Like QuickSort, Merge Sort is a Divide and Conquer algorithm. It divides the input array into two
halves, calls itself for the two halves, and then merges the two sorted halves. The merge()
function is used for merging two halves. The merge(arr, l, m, r) is a key process that assumes
that arr[l..m] and arr[m+1..r] are sorted and merges the two sorted sub-arrays into one.
MergeSort(arr[], l, r)
If r > l
1. Find the middle point to divide the array into two halves:
middle m = l+ (r-l)/2
2. Call mergeSort for first half:
Call mergeSort(arr, l, m)
3. Call mergeSort for second half:
Call mergeSort(arr, m+1, r)
4. Merge the two halves sorted in step 2 and 3:
Call merge(arr, l, m, r)
Python3
def mergeSort(arr):
if len(arr) > 1:
mid = len(arr)//2
# Dividing the array elements
L = arr[:mid]
# into 2 halves
R = arr[mid:]
mergeSort(L)
mergeSort(R)
i=j=k=0
arr[k] = L[i]
i += 1
else:
arr[k] = R[j]
j += 1
k += 1
arr[k] = L[i]
i += 1
k += 1
while j < len(R):
arr[k] = R[j]
j += 1
k += 1
def printList(arr):
for i in range(len(arr)):
print()
# Driver Code
if __name__ == '__main__':
printList(arr)
mergeSort(arr)
printList(arr)
Output
Given array is
12 11 13 5 6 7
5 6 7 11 12 13
The key process in quickSort is partition(). Target of partitions is, given an array and an element
x of array as pivot, put x at its correct position in sorted array and put all smaller elements
(smaller than x) before x, and put all greater elements (greater than x) after x. All this should be
done in linear time.
/* This function takes last element as pivot, places the pivot element at its correct position in
sorted array, and places all smaller (smaller than pivot) to left of pivot and all greater elements
to right of pivot */
partition (arr[], low, high)
{
// pivot (Element to be placed at right position)
pivot = arr[high];
i = (low – 1) // Index of smaller element and indicates the
// right position of pivot found so far
for (j = low; j <= high- 1; j++){
// If current element is smaller than the pivot
if (arr[j] < pivot){
i++; // increment index of smaller element
swap arr[i] and arr[j]
}
}
swap arr[i + 1] and arr[high])
return (i + 1)
}
Python3
# an array respectively
pivot_index = start
pivot = array[pivot_index]
start += 1
end -= 1
return end
# is at right place
quick_sort(start, p - 1, array)
# Driver code
array = [ 10, 7, 8, 9, 1, 5 ]
Output