0% found this document useful (0 votes)
28 views27 pages

FDS Answer Bank 1-10

Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
28 views27 pages

FDS Answer Bank 1-10

Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 27

1] Radix Sort Explanation

Radix Sort is a non-comparative sorting algorithm that sorts numbers by processing individual digits.
Here’s a step-by-step explanation using the given numbers: 54, 43, 47, 59, 36, 60, 55, 26, 35, 31.

Step-by-Step Sorting

Initial Array: 54, 43, 47, 59, 36, 60, 55, 26, 35, 31

Sorting by Least Significant Digit (Unit Place):

Buckets:

0: 60

1: 31

2:

3: 43

4: 54

5: 55, 35

6: 36, 26

7: 47

8:

9: 59

Result: 60, 31, 43, 54, 55, 35, 36, 26, 47, 59

Sorting by Most Significant Digit (Tens Place):

Buckets:

0:

1:

2: 26

3: 31, 35, 36

4: 43, 47

5: 54, 55

6: 60

7:

8:

9: 59
Result: 26, 31, 35, 36, 43, 47, 54, 55, 59, 60

When to Use Radix Sort

Radix Sort is particularly useful when:

The range of the numbers (or keys) is not significantly larger than the number of items.

The keys are integers or strings where the length of the keys is relatively small.

You need a stable sort (preserves the relative order of records with equal keys).

Time Complexity

Best Case: (O(n \cdot k))

Average Case: (O(n \cdot k))

Worst Case: (O(n \cdot k))

Where:

(n) is the number of elements.

(k) is the number of digits in the largest number.

2] Singly Linked List Insertion

A singly linked list is a data structure where each node contains data and a reference (or link) to the
next node in the sequence. Here’s how you can insert a node into a singly linked list and count the
number of nodes.

Insertion in a Singly Linked List

Pseudo Code for Insertion at the Beginning:

function insertAtHead(head, value):

newNode = Node(value)

newNode.next = head

head = newNode

return head

Pseudo Code for Insertion at the End:

function insertAtEnd(head, value):


newNode = Node(value)

if head == NULL:

head = newNode

return head

current = head

while current.next != NULL:

current = current.next

current.next = newNode

return head

Pseudo Code for Insertion After a Given Node:

function insertAfter(node, value):

if node == NULL:

return

newNode = Node(value)

newNode.next = node.next

node.next = newNode

Counting Nodes in a Singly Linked List

Pseudo Code for Counting Nodes:

function countNodes(head):

count = 0

current = head

while current != NULL:

count = count + 1

current = current.next

return count

Example Implementation in Python


Here’s how you might implement these operations in Python:

Python

class Node:

def __init__(self, data):

self.data = data

self.next = None

class SinglyLinkedList:

def __init__(self):

self.head = None

def insert_at_head(self, value):

new_node = Node(value)

new_node.next = self.head

self.head = new_node

def insert_at_end(self, value):

new_node = Node(value)

if self.head is None:

self.head = new_node

return

last = self.head

while last.next:

last = last.next

last.next = new_node

def count_nodes(self):

count = 0

current = self.head
while current:

count += 1

current = current.next

return count

# Example usage:

sll = SinglyLinkedList()

sll.insert_at_head(10)

sll.insert_at_end(20)

sll.insert_at_end(30)

print("Number of nodes:", sll.count_nodes())

# Output: Number of nodes: 3

3] Doubly Linked List

A doubly linked list is a type of linked list in which each node contains three fields:

Data: The value stored in the node.

Next: A pointer to the next node in the sequence.

Prev: A pointer to the previous node in the sequence.

Advantages of Doubly Linked List

Bidirectional Traversal: You can traverse the list in both forward and backward directions.

Easier Deletion: Deleting a node is easier because you have a reference to the previous node.

Flexibility: More flexible in terms of operations like insertion and deletion compared to singly linked
lists.

Disadvantages of Doubly Linked List

More Memory: Requires more memory per node because of the additional pointer.

Complexity: More complex to implement and manage due to the additional pointers.

Insertion Algorithm

Insertion at the Beginning:

function insertAtHead(head, value):


newNode = Node(value)

newNode.next = head

newNode.prev = NULL

if head != NULL:

head.prev = newNode

head = newNode

return head

Insertion at the End:

function insertAtEnd(head, value):

newNode = Node(value)

if head == NULL:

newNode.prev = NULL

head = newNode

return head

last = head

while last.next != NULL:

last = last.next

last.next = newNode

newNode.prev = last

return head

Insertion After a Given Node:

function insertAfter(node, value):

if node == NULL:

return

newNode = Node(value)

newNode.next = node.next

node.next = newNode
newNode.prev = node

if newNode.next != NULL:

newNode.next.prev = newNode

Traversal Algorithm

Forward Traversal:

function traverseForward(head):

current = head

while current != NULL:

print(current.data)

current = current.next

Backward Traversal:

function traverseBackward(tail):

current = tail

while current != NULL:

print(current.data)

current = current.prev

Example Implementation in Python

Here’s how you might implement these operations in Python:

Python

class Node:

def __init__(self, data):

self.data = data

self.next = None

self.prev = None
class DoublyLinkedList:

def __init__(self):

self.head = None

def insert_at_head(self, value):

new_node = Node(value)

new_node.next = self.head

if self.head is not None:

self.head.prev = new_node

self.head = new_node

def insert_at_end(self, value):

new_node = Node(value)

if self.head is None:

self.head = new_node

return

last = self.head

while last.next:

last = last.next

last.next = new_node

new_node.prev = last

def traverse_forward(self):

current = self.head

while current:

print(current.data, end=' ')

current = current.next

print()

def traverse_backward(self):
current = self.head

while current and current.next:

current = current.next

while current:

print(current.data, end=' ')

current = current.prev

print()

# Example usage:

dll = DoublyLinkedList()

dll.insert_at_head(10)

dll.insert_at_end(20)

dll.insert_at_end(30)

dll.traverse_forward() # Output: 10 20 30

dll.traverse_backward() # Output: 30 20 10

4] Generalized Linked List for Multivariable Polynomial Representation

A generalized linked list is a versatile data structure that can be used to represent complex structures
like multivariable polynomials. In this context, each node can represent a term of the polynomial,
and the list can handle multiple variables and their respective exponents.

Node Structure

Each node in a generalized linked list for a multivariable polynomial typically contains:

Coefficient: The numerical multiplier of the term.

Exponent: The power to which the variable is raised.

Next: A pointer to the next term (node) in the polynomial.

Variable: The variable associated with the term (optional, depending on implementation).

Here’s a simplified structure in pseudo code:

class Node:

coefficient: int
exponent: int

variable: char

next: Node

sublist: Node

Example

Consider the multivariable polynomial: ( 3x^2y + 4xy^2 + 5 ).

This polynomial can be represented using a generalized linked list as follows:

Top Level List:

Node 1: Coefficient = 3, Exponent = 2, Variable = ‘x’, Sublist -> Node 2

Node 2: Coefficient = 4, Exponent = 1, Variable = ‘x’, Sublist -> Node 3

Node 3: Coefficient = 5, Exponent = 0, Variable = ‘x’, Sublist = NULL

Sublists:

Node 1 Sublist: Coefficient = 1, Exponent = 1, Variable = ‘y’, Next = NULL

Node 2 Sublist: Coefficient = 1, Exponent = 2, Variable = ‘y’, Next = NULL

Advantages

Flexibility: Can represent polynomials with multiple variables and varying degrees.

Dynamic Size: Can easily grow or shrink as terms are added or removed.

Efficient Operations: Insertion, deletion, and traversal operations are efficient.

Disadvantages

Memory Overhead: Requires additional memory for pointers.

Complexity: More complex to implement and manage compared to simple arrays or singly linked
lists.

Algorithm for Insertion and Traversal

Insertion Algorithm:

function insertTerm(head, coefficient, exponent, variable):

newNode = Node(coefficient, exponent, variable)

if head == NULL:
head = newNode

return head

current = head

while current.next != NULL:

current = current.next

current.next = newNode

return head

Traversal Algorithm:

function traversePolynomial(head):

current = head

while current != NULL:

print(current.coefficient, current.variable, "^", current.exponent)

if current.sublist != NULL:

traversePolynomial(current.sublist)

current = current.next

Example Implementation in Python

Here’s how you might implement these operations in Python:

Python

class Node:

def __init__(self, coefficient, exponent, variable):

self.coefficient = coefficient

self.exponent = exponent

self.variable = variable

self.next = None

self.sublist = None
class Polynomial:

def __init__(self):

self.head = None

def insert_term(self, coefficient, exponent, variable):

new_node = Node(coefficient, exponent, variable)

if self.head is None:

self.head = new_node

else:

current = self.head

while current.next:

current = current.next

current.next = new_node

def traverse(self, node=None):

if node is None:

node = self.head

while node:

print(f"{node.coefficient}{node.variable}^{node.exponent}", end=" ")

if node.sublist:

self.traverse(node.sublist)

node = node.next

print()

# Example usage:

poly = Polynomial()

poly.insert_term(3, 2, 'x')

poly.head.sublist = Node(1, 1, 'y')

poly.insert_term(4, 1, 'x')

poly.head.next.sublist = Node(1, 2, 'y')

poly.insert_term(5, 0, 'x')
poly.traverse()

5] Node Structure of Singly Linked List

A singly linked list is a linear data structure where each element (node) points to the next node in the
sequence. Each node contains two parts:

Data: The value stored in the node.

Next: A pointer/reference to the next node in the list.

Node Structure:

class Node:

data: int

next: Node

Node Structure of Circular Singly Linked List

A circular singly linked list is similar to a singly linked list, but the last node points back to the first
node, forming a circle. This means there is no NULL at the end of the list.

Node Structure:

class Node:

data: int

next: Node

In a circular singly linked list, the next pointer of the last node points to the head of the list.

Pseudocode for Concatenation of Two Singly Linked Lists

To concatenate two singly linked lists, we need to link the last node of the first list to the head of the
second list.

Pseudocode:

function concatenateLists(head1, head2):


if head1 == NULL:

return head2

if head2 == NULL:

return head1

current = head1

while current.next != NULL:

current = current.next

current.next = head2

return head1

Example Implementation in Python

Here’s how you might implement these operations in Python:

Python

class Node:

def __init__(self, data):

self.data = data

self.next = None

class SinglyLinkedList:

def __init__(self):

self.head = None

def append(self, data):

new_node = Node(data)

if not self.head:

self.head = new_node

return

last = self.head

while last.next:
last = last.next

last.next = new_node

def concatenate(self, other_list):

if not self.head:

self.head = other_list.head

return

if not other_list.head:

return

last = self.head

while last.next:

last = last.next

last.next = other_list.head

def display(self):

current = self.head

while current:

print(current.data, end=" -> ")

current = current.next

print("None")

# Example usage:

list1 = SinglyLinkedList()

list1.append(1)

list1.append(2)

list1.append(3)

list2 = SinglyLinkedList()

list2.append(4)

list2.append(5)

list2.append(6)
list1.concatenate(list2)

list1.display() # Output: 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> None

This code defines a Node class and a SinglyLinkedList class with methods to append nodes,
concatenate two lists, and display the list. The concatenate method links the last node of the first list
to the head of the second list.

6] Necessity of Prefix and Postfix Notation

Prefix (Polish) Notation and Postfix (Reverse Polish) Notation are ways to write expressions without
the need for parentheses to denote operation order. This makes them particularly useful for
computer processing.

Advantages:

• No Parentheses Needed: The order of operations is unambiguous without parentheses.

• Efficient Computation: Easier for computers to evaluate using stacks.

• Consistent Order: Operators appear in a consistent order relative to their operands.

Postfix Evaluation Using Stack

Algorithm for Postfix Evaluation:

1. Initialize an empty stack.

2. Read the postfix expression from left to right.

3. For each element:

o If the element is an operand, push it onto the stack.

o If the element is an operator, pop the top two elements from the stack, apply the
operator, and push the result back onto the stack.

4. The result will be the only element left in the stack.

Example Evaluation

Given postfix expression: A B C * D E F ^ / G * - H +


With values: A=7, B=2, C=5, D=17, E=3, F=4, G=3, H=6

Step-by-Step Evaluation:

1. Initial Stack: []

2. Read A (7): [7]

3. Read B (2): [7, 2]

4. Read C (5): [7, 2, 5]

5. Read *: Pop 5 and 2, push 10: [7, 10]

6. Read D (17): [7, 10, 17]


7. Read E (3): [7, 10, 17, 3]

8. Read F (4): [7, 10, 17, 3, 4]

9. Read ^: Pop 4 and 3, push 81: [7, 10, 17, 81]

10. Read /: Pop 81 and 17, push 0.2099: [7, 10, 0.2099]

11. Read G (3): [7, 10, 0.2099, 3]

12. Read *: Pop 3 and 0.2099, push 0.6297: [7, 10, 0.6297]

13. Read -: Pop 0.6297 and 10, push 9.3703: [7, 9.3703]

14. Read H (6): [7, 9.3703, 6]

15. Read +: Pop 6 and 9.3703, push 15.3703: [7, 15.3703]

Final Result: 15.3703

Pseudocode for Postfix Evaluation

function evaluatePostfix(expression):

stack = createEmptyStack()

for token in expression:

if token is operand:

stack.push(token)

else if token is operator:

operand2 = stack.pop()

operand1 = stack.pop()

result = applyOperator(token, operand1, operand2)

stack.push(result)

return stack.pop()

function applyOperator(operator, operand1, operand2):

if operator == '+':

return operand1 + operand2

if operator == '-':

return operand1 - operand2

if operator == '*':

return operand1 * operand2

if operator == '/':
return operand1 / operand2

if operator == '^':

return operand1 ^ operand2

This pseudocode outlines the process of evaluating a postfix expression using a stack, ensuring
efficient and accurate computation.

7] Step-by-Step Conversion from Infix to Postfix Using Stack

To convert an infix expression to a postfix expression, we use a stack to help manage the operators
and ensure they are applied in the correct order. Here’s how you can convert the given infix
expression (( ( a / ( b - c + d ) ) * ( e - a ) * c )) to postfix notation step-by-step:

Infix Expression: (( ( a / ( b - c + d ) ) * ( e - a ) * c ))

Steps:

1. Initialize an empty stack for operators and an empty list for the output (postfix expression).

2. Scan the infix expression from left to right.

Detailed Steps:

1. Read ‘(’: Push to stack.

o Stack: [‘(’]

o Output: []

2. Read ‘(’: Push to stack.

o Stack: [‘(’, ‘(’]

o Output: []

3. Read ‘a’: Append to output.

o Stack: [‘(’, ‘(’]

o Output: [‘a’]

4. Read ‘/’: Push to stack.

o Stack: [‘(’, ‘(’, ‘/’]

o Output: [‘a’]

5. Read ‘(’: Push to stack.

o Stack: [‘(’, ‘(’, ‘/’, ‘(’]

o Output: [‘a’]

6. Read ‘b’: Append to output.

o Stack: [‘(’, ‘(’, ‘/’, ‘(’]

o Output: [‘a’, ‘b’]


7. Read ‘-’: Push to stack.

o Stack: [‘(’, ‘(’, ‘/’, ‘(’, ‘-’]

o Output: [‘a’, ‘b’]

8. Read ‘c’: Append to output.

o Stack: [‘(’, ‘(’, ‘/’, ‘(’, ‘-’]

o Output: [‘a’, ‘b’, ‘c’]

9. Read ‘+’: Pop ‘-’ from stack (lower precedence) and push ‘+’.

o Stack: [‘(’, ‘(’, ‘/’, ‘(’]

o Output: [‘a’, ‘b’, ‘c’, ‘-’]

o Stack: [‘(’, ‘(’, ‘/’, ‘(’, ‘+’]

10. Read ‘d’: Append to output.

o Stack: [‘(’, ‘(’, ‘/’, ‘(’, ‘+’]

o Output: [‘a’, ‘b’, ‘c’, ‘-’, ‘d’]

11. Read ‘)’: Pop and append until ‘(’.

o Stack: [‘(’, ‘(’, ‘/’]

o Output: [‘a’, ‘b’, ‘c’, ‘-’, ‘d’, ‘+’]

12. Read ‘)’: Pop and append until ‘(’.

o Stack: [‘(’]

o Output: [‘a’, ‘b’, ‘c’, ‘-’, ‘d’, ‘+’, ‘/’]

13. Read ‘*’: Push to stack.

o Stack: [‘(’, ‘*’]

o Output: [‘a’, ‘b’, ‘c’, ‘-’, ‘d’, ‘+’, ‘/’]

14. Read ‘(’: Push to stack.

o Stack: [‘(’, ‘*’, ‘(’]

o Output: [‘a’, ‘b’, ‘c’, ‘-’, ‘d’, ‘+’, ‘/’]

15. Read ‘e’: Append to output.

o Stack: [‘(’, ‘*’, ‘(’]

o Output: [‘a’, ‘b’, ‘c’, ‘-’, ‘d’, ‘+’, ‘/’, ‘e’]

16. Read ‘-’: Push to stack.

o Stack: [‘(’, ‘*’, ‘(’, ‘-’]

o Output: [‘a’, ‘b’, ‘c’, ‘-’, ‘d’, ‘+’, ‘/’, ‘e’]


17. Read ‘a’: Append to output.

o Stack: [‘(’, ‘*’, ‘(’, ‘-’]

o Output: [‘a’, ‘b’, ‘c’, ‘-’, ‘d’, ‘+’, ‘/’, ‘e’, ‘a’]

18. Read ‘)’: Pop and append until ‘(’.

o Stack: [‘(’, ‘*’]

o Output: [‘a’, ‘b’, ‘c’, ‘-’, ‘d’, ‘+’, ‘/’, ‘e’, ‘a’, ‘-’]

19. Read ‘*’: Pop and append ‘*’ (same precedence).

o Stack: [‘(’]

o Output: [‘a’, ‘b’, ‘c’, ‘-’, ‘d’, ‘+’, ‘/’, ‘e’, ‘a’, ‘-’, ‘*’]

o Stack: [‘(’, ‘*’]

20. Read ‘c’: Append to output.

o Stack: [‘(’, ‘*’]

o Output: [‘a’, ‘b’, ‘c’, ‘-’, ‘d’, ‘+’, ‘/’, ‘e’, ‘a’, ‘-’, ‘*’, ‘c’]

21. Read ‘)’: Pop and append until ‘(’.

o Stack: []

o Output: [‘a’, ‘b’, ‘c’, ‘-’, ‘d’, ‘+’, ‘/’, ‘e’, ‘a’, ‘-’, ‘', ‘c’, '’]

Final Postfix Expression:

abc-d+/ea-*c*

This step-by-step process ensures that the operators are applied in the correct order, respecting their
precedence and associativity rules.

8] Fibonacci Search

Fibonacci Search is a comparison-based search algorithm that uses Fibonacci numbers to divide the
array into smaller sections. It is similar to binary search but uses Fibonacci numbers to determine the
split points, which can be more efficient in certain scenarios.

Key Points:

• Divide and Conquer: Like binary search, it divides the array into smaller parts.

• Fibonacci Numbers: Uses Fibonacci numbers to determine the split points.

• Efficiency: Can be more efficient than binary search in some cases, especially when dealing
with non-uniform memory access.

Pseudocode for Fibonacci Search

function fibonacciSearch(arr, x, n):

fibMMm2 = 0 // (m-2)'th Fibonacci number


fibMMm1 = 1 // (m-1)'th Fibonacci number

fibM = fibMMm2 + fibMMm1 // m'th Fibonacci number

// fibM is the smallest Fibonacci number greater than or equal to n

while (fibM < n):

fibMMm2 = fibMMm1

fibMMm1 = fibM

fibM = fibMMm2 + fibMMm1

offset = -1

// While there are elements to be inspected

while (fibM > 1):

i = min(offset + fibMMm2, n-1)

// If x is greater than the value at index fibMMm2, cut the subarray array from offset to i

if (arr[i] < x):

fibM = fibMMm1

fibMMm1 = fibMMm2

fibMMm2 = fibM - fibMMm1

offset = i

// If x is less than the value at index fibMMm2, cut the subarray after i+1

else if (arr[i] > x):

fibM = fibMMm2

fibMMm1 = fibMMm1 - fibMMm2

fibMMm2 = fibM - fibMMm1

// Element found

else:

return i
// Comparing the last element with x

if (fibMMm1 and arr[offset + 1] == x):

return offset + 1

// Element not found

return -1

Example: Search for 54 in the List

Given list: {46, 34, 63, 31, 54, 67, 76, 68, 73, 32}

1. Sort the List: {31, 32, 34, 46, 54, 63, 67, 68, 73, 76}

2. Initialize Fibonacci Numbers: fibMMm2 = 0, fibMMm1 = 1, fibM = 1

3. Find the smallest Fibonacci number greater than or equal to 10:

o fibMMm2 = 1, fibMMm1 = 1, fibM = 2

o fibMMm2 = 1, fibMMm1 = 2, fibM = 3

o fibMMm2 = 2, fibMMm1 = 3, fibM = 5

o fibMMm2 = 3, fibMMm1 = 5, fibM = 8

o fibMMm2 = 5, fibMMm1 = 8, fibM = 13

4. Search Process:

o offset = -1

o i = min(-1 + 5, 9) = 4 (compare arr[4] with 54)

o arr[4] = 54, which matches the search element.

Result: The element 54 is found at index 4 in the sorted list.

Analysis of Fibonacci Search

• Time Complexity: The time complexity of Fibonacci search is (O(\log n)), similar to binary
search1.

• Space Complexity: The space complexity is (O(1)) as it uses a constant amount of extra
space.

• Advantages:

o Efficient for large datasets.

o Can be more cache-friendly than binary search due to non-uniform memory access
patterns.

• Disadvantages:
o Requires the array to be sorted.

o Slightly more complex to implement compared to binary search.

Fibonacci search is particularly useful in scenarios where the cost of comparison is high, and the
array is large and sorted.

9] Quick Sort Algorithm

Quick Sort is a highly efficient sorting algorithm based on the divide-and-conquer principle. It works
by selecting a ‘pivot’ element from the array and partitioning the other elements into two sub-
arrays, according to whether they are less than or greater than the pivot. The sub-arrays are then
sorted recursively.

Pseudocode for Quick Sort

function quickSort(arr, low, high):

if low < high:

pivotIndex = partition(arr, low, high)

quickSort(arr, low, pivotIndex - 1)

quickSort(arr, pivotIndex + 1, high)

function partition(arr, low, high):

pivot = arr[high]

i = low - 1

for j = low to high - 1:

if arr[j] <= pivot:

i=i+1

swap arr[i] with arr[j]

swap arr[i + 1] with arr[high]

return i + 1

Example: Sorting the Array {55, 79, 81, 75, 93, 23, 66, 21}

Let’s go through the steps of sorting this array using Quick Sort:

1. Initial Array: {55, 79, 81, 75, 93, 23, 66, 21}

2. First Call: quickSort(arr, 0, 7)

o Pivot: 21

o Partition: {21, 79, 81, 75, 93, 23, 66, 55}

o Pivot Index: 0
3. Recursive Calls:

o quickSort(arr, 0, -1) (Base case, no action)

o quickSort(arr, 1, 7)

4. Second Call: quickSort(arr, 1, 7)

o Pivot: 55

o Partition: {21, 23, 81, 75, 93, 79, 66, 55}

o Pivot Index: 2

5. Recursive Calls:

o quickSort(arr, 1, 1) (Base case, no action)

o quickSort(arr, 3, 7)

6. Third Call: quickSort(arr, 3, 7)

o Pivot: 55

o Partition: {21, 23, 55, 75, 93, 79, 66, 81}

o Pivot Index: 2

7. Recursive Calls:

o quickSort(arr, 3, 1) (Base case, no action)

o quickSort(arr, 3, 7)

8. Fourth Call: quickSort(arr, 3, 7)

o Pivot: 81

o Partition: {21, 23, 55, 66, 75, 79, 81, 93}

o Pivot Index: 6

9. Recursive Calls:

o quickSort(arr, 3, 5)

o quickSort(arr, 7, 7) (Base case, no action)

10. Fifth Call: quickSort(arr, 3, 5)

o Pivot: 79

o Partition: {21, 23, 55, 66, 75, 79, 81, 93}

o Pivot Index: 5

11. Recursive Calls:

o quickSort(arr, 3, 4)

o quickSort(arr, 6, 5) (Base case, no action)


12. Sixth Call: quickSort(arr, 3, 4)

o Pivot: 75

o Partition: {21, 23, 55, 66, 75, 79, 81, 93}

o Pivot Index: 4

13. Recursive Calls:

o quickSort(arr, 3, 3) (Base case, no action)

o quickSort(arr, 5, 4) (Base case, no action)

Final Sorted Array: {21, 23, 55, 66, 75, 79, 81, 93}

Analysis of Quick Sort

• Time Complexity:

o Best Case: (O(n \log n))

o Average Case: (O(n \log n))

o Worst Case: (O(n^2)) (occurs when the smallest or largest element is always chosen
as the pivot)

• Space Complexity: (O(\log n)) due to the recursive stack space.

Quick Sort is generally faster in practice compared to other (O(n \log n)) algorithms like Merge Sort
and Heap Sort, especially for large datasets. However, its performance can degrade to (O(n^2)) if the
pivot selection is poor.

10] Selection Sort Algorithm

Selection Sort is a simple comparison-based sorting algorithm. It works by repeatedly finding the
minimum element from the unsorted part of the array and moving it to the beginning.

Pseudocode for Selection Sort

function selectionSort(arr):

n = length(arr)

for i from 0 to n-1:

minIndex = i

for j from i+1 to n:

if arr[j] < arr[minIndex]:

minIndex = j

swap arr[i] with arr[minIndex]


Example: Sorting the Array {33, 66, 11, 24, 65, 32, 86, 45}

Let’s go through the steps of sorting this array using Selection Sort:

Initial Array: {33, 66, 11, 24, 65, 32, 86, 45}

Pass 1:

Find the minimum element from index 0 to 7: 11

Swap 11 with the element at index 0

Array: {11, 66, 33, 24, 65, 32, 86, 45}

Pass 2:

Find the minimum element from index 1 to 7: 24

Swap 24 with the element at index 1

Array: {11, 24, 33, 66, 65, 32, 86, 45}

Pass 3:

Find the minimum element from index 2 to 7: 32

Swap 32 with the element at index 2

Array: {11, 24, 32, 66, 65, 33, 86, 45}

Pass 4:

Find the minimum element from index 3 to 7: 33

Swap 33 with the element at index 3

Array: {11, 24, 32, 33, 65, 66, 86, 45}

Pass 5:

Find the minimum element from index 4 to 7: 45

Swap 45 with the element at index 4

Array: {11, 24, 32, 33, 45, 66, 86, 65}

Pass 6:

Find the minimum element from index 5 to 7: 65

Swap 65 with the element at index 5

Array: {11, 24, 32, 33, 45, 65, 86, 66}

Pass 7:

Find the minimum element from index 6 to 7: 66

Swap 66 with the element at index 6


Array: {11, 24, 32, 33, 45, 65, 66, 86}

Pass 8:

The last element is already in place

Array: {11, 24, 32, 33, 45, 65, 66, 86}

Final Sorted Array: {11, 24, 32, 33, 45, 65, 66, 86}

Time and Space Complexity

Time Complexity:

Best Case: (O(n^2))

Average Case: (O(n^2))

Worst Case: (O(n^2))

The time complexity is (O(n^2)) in all cases because it always performs (n(n-1)/2) comparisons,
regardless of the initial order of the array.

Space Complexity: (O(1))

Selection Sort is an in-place sorting algorithm, meaning it requires only a constant amount of
additional memory space.

You might also like